2010-04-07 7 views
9

Supongamos, tengo un conector conectado después de escribir el código ..¿Cómo saber si el cliente ha terminado en los zócalos

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

¿Cómo puedo saber en el lado del servidor que el cliente haya salido.

Todo mi programa realmente hace lo siguiente ..

  • Acepta una conexión desde el cliente
  • inicia un nuevo hilo que lee los mensajes de ese cliente en particular y luego transmitido este mensaje a todos los clientes conectados.

Si desea ver el código completo ... En este código completo. También estoy luchando con un problema más que cuando mato a un cliente con Ctrl + C, mi servidor termina abruptamente .. Sería bueno si alguien podría sugerir cuál es el problema ..

#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"); 
      } 
     } 
    } 
} 

Mi cliente es este lado (Maye ser irrelevante mientras que contesta a mi pregunta)

#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; 
} 
+0

¿Puede incluir un poco más de código? ¿Qué tipo de socket es este? ¿Qué quiere decir con el lado del servidor? – WhirlWind

+0

He agregado el código completo del lado del servidor. Acabo de empezar a aprender la programación de socket, por lo que el lado del servidor no es tan robusto ... Cualquier sugerencia es bienvenida. –

Respuesta

12

Después de aceptar la conexión, su recv() en el zócalo devolverá 0 o -1 en casos especiales.

Extracto de recv(3) man page:

Al completar con éxito, recv() devolverá la longitud del mensaje en bytes. Si no hay mensajes disponibles para recibir y el igual tiene realizó un apagado ordenado, recv() devolverá 0. De lo contrario, -1 será devuelto y errno configurado para indicar el error .

Por lo tanto, si su cliente salió con gracia, obtendrá 0 de recv() en algún momento. Si la conexión se perdió de alguna manera, también puede obtener -1 y comprobar si el error correspondiente le indica si se perdió la conexión de algún otro error. Ver más detalles en recv(3) man page.

Editar:

veo que está utilizando read(). Aún así, se aplican las mismas reglas que con recv().

Su servidor también puede fallar al intentar write() a sus clientes. Si su cliente se desconecta write() devolverá -1 y el error probablemente se establecerá en EPIPE. Además, la señal SIGPIPE se enviará a su proceso y lo matará si no bloquea/ignora esta señal. Y no lo hace como yo veo y es por eso que su servidor termina cuando el cliente presiona Ctrl-C. Ctrl-C termina el cliente, por lo tanto, cierra el socket del cliente y hace que el servidor write() falle.

Consulte la respuesta de mark4o para obtener una explicación detallada de lo que podría salir mal.

+0

No, eso es solo si el lado del servidor cerró el socket. – WhirlWind

+0

¿Cómo es que vino? Después de 'aceptar()' el socket que lee de él como desde cualquier otro socket. – pajton

+0

También preste atención a 'errno == EPIPE' cuando * escribe * en el socket. –

8

Si el programa cliente sale, entonces el SO del cliente cerrará su extremo del socket. Cuando llame al recv(), devolverá 0, o -1 con errno ECONNRESET si se recibió un TCP RST (por ejemplo, porque intentó enviar datos después de que el cliente cerró). Si todo el equipo del cliente se cae o la red se desconecta, entonces, en ese caso, es posible que no reciba nada si el servidor no está intentando enviar nada; si eso es importante de detectar, puede enviar algunos datos periódicamente o establecer la opción de socket SO_KEEPALIVE usando setsockopt() para forzarlo a enviar un paquete sin datos después de largos períodos (horas) de inactividad. Cuando no se recibe acuse de recibo, recv() devolverá -1 con errno ETIMEDOUT u otro error si hay información más específica disponible.

Además, si intenta enviar datos en un socket que se ha desconectado, de forma predeterminada la señal SIGPIPE terminará su programa. Esto se puede evitar estableciendo la acción de señal SIGPIPE en SIG_IGN (ignorar), o usando send() con el indicador MSG_NOSIGNAL en los sistemas que lo admiten (Linux).

+0

+1 para dar una explicación detallada de situaciones más especiales. – pajton

+0

Incluso si esto es bastante antiguo, la explicación de por qué mi proceso finaliza si un cliente se desconectó simplemente me salvó el día. ¡Muchas gracias! – ZeroTek

Cuestiones relacionadas