2010-04-07 23 views
9

Supponiamo, ho un socket connesso dopo aver scritto questo codice ..Come sapere se il cliente ha terminato in zoccoli

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

Come posso sapere sul lato server che client è terminato.

mio intero programma in realtà fa il seguente ..

  • accetta una connessione dal client
  • Inizia un nuovo thread che legge i messaggi da quel particolare client e quindi trasmesso questo messaggio a tutti i client connessi.

Se si desidera vedere l'intero codice ... In questo intero codice. Sono anche alle prese con un altro problema che ogni volta che uccido un cliente con Ctrl + C, il mio server si interrompe bruscamente .. Sarebbe bello se qualcuno potrebbe suggerire quale sia il 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"); 
      } 
     } 
    } 
} 

Il mio cliente lato è questo (Maye essere irrilevante, mentre rispondere alla mia domanda)

#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

Puoi includere un po 'di codice in più? Che tipo di presa è questa? Cosa intendi per lato server? – WhirlWind

+0

Ho aggiunto il codice lato server completo. Ho appena iniziato a imparare la programmazione dei socket in modo che il lato server non sia così robusto ... Qualsiasi suggerimento è benvenuto. –

risposta

12

Dopo aver accettato la connessione, il recv() sulla presa restituirà 0 o -1 in casi particolari.

Estratto dal recv(3) man page:

In caso di superamento, recv() deve restituire la lunghezza del messaggio in byte. Se nessun messaggio è disponibile da ricevere e il peer ha eseguito un arresto regolare, recv() deve restituire 0. In caso contrario, -1 sarà restituito e errno impostato per indicare l'errore .

Quindi, se il client è uscito con grazia, otterrete 0 da recv() ad un certo punto. Se la connessione è stata in qualche modo persa, si potrebbe anche ottenere -1 e il controllo di errno appropriato ti dirà se la connessione è stata persa di qualche altro errore. Vedi maggiori dettagli a recv(3) man page.

Edit:

Vedo che si sta utilizzando read(). Ancora, si applicano le stesse regole di recv().

Il server può anche non riuscire quando si tenta di write() ai client. Se il client si disconnette, write() restituirà -1 e l'errno verrebbe probabilmente impostato su EPIPE. Inoltre, il segnale SIGPIPE verrà inviato al processo e lo ucciderà se non si blocca/ignora questo segnale. E non è così che vedo e questo è il motivo per cui il tuo server termina quando il cliente preme Ctrl-C. Ctrl-C termina il client, quindi chiude il socket del client e fa fallire il server write() del server.

Vedere la risposta di mark4o per una bella spiegazione dettagliata di cos'altro potrebbe andare storto.

+0

No, è solo se il lato server ha chiuso il socket. – WhirlWind

+0

Come mai? Dopo aver 'accept()' il socket che hai letto come da qualsiasi altro socket. – pajton

+0

Prestare attenzione anche a 'errno == EPIPE' quando * si scrive * sul socket. –

8

Se il programma client termina, il sistema operativo sul client chiuderà la sua estremità del socket. Quando si chiama recv(), verrà restituito 0 o -1 con errno ECONNRESET se è stato ricevuto un RST TCP (ad esempio perché si è tentato di inviare dati dopo la chiusura del client). Se l'intera macchina client si interrompe o la rete si disconnette, in tal caso non si può ricevere nulla se il server non sta tentando di inviare nulla; se ciò è importante da rilevare, è possibile inviare periodicamente alcuni dati oppure impostare l'opzione socket SO_KEEPALIVE utilizzando setsockopt() per forzare l'invio di un pacchetto senza dati dopo lunghi periodi (inattivi) di inattività. Se non viene ricevuto alcun riscontro, recv() restituirà -1 con errno ETIMEDOUT o un altro errore se sono disponibili informazioni più specifiche.

Inoltre, se si tenta di inviare dati su una presa che è stata disconnessa, per impostazione predefinita il segnale SIGPIPE interromperà il programma. Ciò può essere evitato impostando l'azione del segnale SIGPIPE su SIG_IGN (ignora) o usando send() con il flag MSG_NOSIGNAL sui sistemi che lo supportano (Linux).

+0

+1 per dare spiegazione dettagliata delle situazioni più speciale. – pajton

+0

Anche se questo è abbastanza vecchia, la spiegazione sul perché il mio processo viene terminato se un client disconnessi appena salvato la mia giornata! Grazie mille! – ZeroTek

Problemi correlati