Как знать, завершил ли клиент в сокетах

Предположим, у меня есть подключенный сокет после написания этого кода..

if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
    perror("accept failed\n");
    exit(1);
}

Как я могу знать в стороне сервера, что клиент вышел.

Моя целая программа на самом деле делает следующее..

  • Принимает соединение от клиента
  • Начинает новую дискуссию, которая читает сообщения от того конкретного клиента и затем широковещательно передает это сообщение всем связанным клиентам.

Если Вы хотите видеть целый код... В этом целом коде. Я также борюсь с еще одной проблемой, что каждый раз, когда я уничтожаю клиент с Ctrl+C, мой сервер завершается резко.. Было бы хорошо, если кто-либо мог бы предложить, какова проблема..

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>

/*CONSTANTS*/
#define DEFAULT_PORT 10000 
#define LISTEN_QUEUE_LIMIT 6
#define TOTAL_CLIENTS 10
#define CHAR_BUFFER 256

/*GLOBAL VARIABLE*/
int current_client = 0;
int connected_clients[TOTAL_CLIENTS];
extern int errno;

void *client_handler(void * socket_d);

int main(int argc, char *argv[])
{
    struct sockaddr_in server_addr;/* structure to hold server's address*/
    int    socket_d;             /* listening socket descriptor       */
    int    port;           /* protocol port number              */
    int    option_value;   /* needed for setsockopt             */
    pthread_t tid[TOTAL_CLIENTS];
    port = (argc > 1)?atoi(argv[1]):DEFAULT_PORT;

    /* Socket Server address structure */
    memset((char *)&server_addr, 0, sizeof(server_addr)); 
    server_addr.sin_family = AF_INET;               /* set family to Internet */
    server_addr.sin_addr.s_addr = INADDR_ANY;       /* set the local IP address */
    server_addr.sin_port = htons((u_short)port);    /* Set port */

    /* Create socket */
    if ( (socket_d = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "socket creation failed\n");
        exit(1);
    }

    /* Make listening socket's port reusable */
    if (setsockopt(socket_d, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, 
                sizeof(option_value)) < 0) {
        fprintf(stderr, "setsockopt failure\n");
        exit(1);
    }

    /* Bind a local address to the socket */
    if (bind(socket_d, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        fprintf(stderr, "bind failed\n");
        exit(1);
    }

    /* Specify size of request queue */
    if (listen(socket_d, LISTEN_QUEUE_LIMIT) < 0) {
        fprintf(stderr, "listen failed\n");
        exit(1);
    }

    memset(connected_clients,0,sizeof(int)*TOTAL_CLIENTS);

    for (;;)
    {
        struct sockaddr_in client_addr;    /* structure to hold client's address*/
        int    alen = sizeof(client_addr); /* length of address                 */
        int    sd;                /* connected socket descriptor */

        if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
        {
            perror("accept failed\n");
            exit(1);
        }
        else printf("\n I got a connection from (%s , %d)\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));

        if (pthread_create(&tid[current_client],NULL,(void *)client_handler,(void *)sd) != 0)
        {
            perror("pthread_create error");
            continue;
        }
        connected_clients[current_client]=sd;
        current_client++; /*Incrementing Client number*/
    }

    return 0;
}

void *client_handler(void *connected_socket)
{
    int sd;
    sd = (int)connected_socket;
    for ( ; ; ) 
    {
        ssize_t n;
        char buffer[CHAR_BUFFER];
        for ( ; ; )
        {
            if (n = read(sd, buffer, sizeof(char)*CHAR_BUFFER) == -1)
            {
                perror("Error reading from client");
                pthread_exit(1);
            }
            int i=0;
            for (i=0;i<current_client;i++)
            {
                if (write(connected_clients[i],buffer,sizeof(char)*CHAR_BUFFER) == -1)
                    perror("Error sending messages to a client while multicasting");
            }
        }
    }
}

Моя сторона клиента - это (Maye быть не важной при ответе на мой вопрос)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <string.h>
#include <stdlib.h>

void error(char *msg)
{
    perror(msg);
    exit(0);
}

void *listen_for_message(void * fd)
{
    int sockfd = (int)fd;
    int n;
    char buffer[256];
    bzero(buffer,256);
    printf("YOUR MESSAGE: ");
    fflush(stdout);
    while (1)
    {
        n = read(sockfd,buffer,256);
        if (n < 0) 
            error("ERROR reading from socket");
        if (n == 0) pthread_exit(1);
        printf("\nMESSAGE BROADCAST: %sYOUR MESSAGE: ",buffer);
        fflush(stdout);
    }
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    pthread_t read_message;
    char buffer[256];
    if (argc < 3) {
        fprintf(stderr,"usage %s hostname port\n", argv[0]);
        exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
            (char *)&serv_addr.sin_addr.s_addr,
            server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");
    bzero(buffer,256);
    if (pthread_create(&read_message,NULL,(void *)listen_for_message,(void *)sockfd) !=0 )
    {
        perror("error creating thread");
    }
    while (1)
    {
        fgets(buffer,255,stdin);
        n = write(sockfd,buffer,256);
        if (n < 0) 
            error("ERROR writing to socket");
        bzero(buffer,256);
    }
    return 0;
}
9
задан Abhijeet Rastogi 7 April 2010 в 15:11
поделиться

2 ответа

После принятия соединения ваш recv () на сокете в особых случаях возвращает 0 или -1.

Выдержка из справочной страницы recv (3) :

После успешного завершения recv () вернет длину сообщения в байтах.Если нет доступных сообщений для получения и одноранговый узел выполнил упорядоченное завершение работы, recv () должен вернуть 0. В противном случае -1 должно быть возвращается, а errno устанавливается, чтобы указать на ошибку .

Итак, если ваш клиент завершил работу корректно, вы в какой-то момент получите 0 из recv () . Если соединение было каким-то образом потеряно, вы также можете получить -1, и проверка соответствующего errno сообщит вам, было ли потеряно соединение или произошла какая-то другая ошибка. См. Более подробную информацию на странице руководства recv (3) .

Изменить:

Я вижу, что вы используете read () . Тем не менее, применяются те же правила, что и для recv () .

Ваш сервер также может дать сбой при попытке write () вашим клиентам. Если ваш клиент отключается, write () вернет -1, а для ошибки, вероятно, будет установлено значение EPIPE . Также, сигнал SIGPIPE будет отправлен вашему процессу и убьет его, если вы не заблокируете / проигнорируете этот сигнал. А вы этого не делаете, как я вижу, и именно поэтому ваш сервер завершает работу, когда клиент нажимает Ctrl-C. Ctrl-C завершает работу клиента, поэтому закрывает клиентский сокет и вызывает сбой write () вашего сервера.

См. Ответ mark4o для подробного объяснения того, что еще может пойти не так.

14
ответ дан 4 December 2019 в 08:51
поделиться

Если клиентская программа завершает работу, то ОС на клиенте закрывает свой конец сокета. Когда вы вызываете recv () , он возвращает 0 или -1 с ошибкой ECONNRESET , если был получен TCP RST (например, из-за того, что вы пытались отправить данные после закрытия клиента) . Если вся клиентская машина выходит из строя или сеть отключается, тогда вы можете ничего не получить, если сервер не пытается что-либо отправить; если это важно для обнаружения, вы можете либо периодически отправлять некоторые данные, либо установить параметр сокета SO_KEEPALIVE с помощью setsockopt () , чтобы заставить его отправлять пакет без данных через длительные периоды (часов) бездействия. Если подтверждение не получено, recv () вернет -1 с ошибкой ETIMEDOUT или другой ошибкой, если доступна более конкретная информация.

Кроме того, если вы попытаетесь отправить данные через сокет, который был отключен, по умолчанию сигнал SIGPIPE завершит вашу программу. Этого можно избежать, установив для действия сигнала SIGPIPE значение SIG_IGN (игнорировать) или используя send () с флагом MSG_NOSIGNAL в системах, которые его поддерживают (Linux).

10
ответ дан 4 December 2019 в 08:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: