如何知道客户端是否已在套接字中终止

假设在编写此代码后我有一个连接的套接字..

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

如何在服务器端知道客户端已退出。

我的整个程序实际上做了以下..

  • 接受来自客户端的连接
  • 启动一个新线程,该线程从该特定客户端读取消息,然后将此消息广播到所有连接的客户端。

如果你想看到整个代码……在这整个代码中。 我还在努力解决另外一个问题,每当我用Ctrl + C杀死一个客户端时,我的服务器突然终止.. 如果有人能提出问题是什么 ,那就太好了。

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  /*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  #include  #include  #include  #include  #include  #include  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 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; } 

接受连接后,套接字上的recv()在特殊情况下将返回0或-1。

摘自recv(3)手册页 :

成功完成后,recv()将以字节为单位返回消息的长度。 如果没有可接收的消息且对等体已执行有序关闭,则recv()将返回0.否则,将返回-1并设置errno以指示错误。

因此,如果您的客户端正常退出,您将在某个时刻从recv()获得0。 如果连接以某种方式丢失,你也可能得到-1并检查适当的errno会告诉你连接是否丢失了一些其他错误。 有关详细信息,请参见recv(3)手册页 。

编辑:

我看到你正在使用read() 。 仍然,与recv()相同的规则适用。

尝试向客户端write()时,您的服务器也会失败。 如果客户端断开连接, write()将返回-1,而errno可能会设置为EPIPE 。 此外,如果您不阻止/忽略此信号, SIGPIPE信号将发送给您进程并杀死他。 而且你不像我看到的那样,这就是当客户端按下Ctrl-C时服务器终止的原因。 Ctrl-C终止客户端,因此关闭客户端套接字并使服务器的write()失败。

请参阅mark4o的答案,了解其他可能出错的详细解释。

如果客户端程序退出,则客户端上的操作系统将关闭其套接字的末尾。 当你调用recv()时,如果收到TCP RST,它将返回0或者带有errno ECONNRESET -1(例如,因为你试图在客户端关闭后发送数据)。 如果整个客户端计算机出现故障,或者网络断开连接,那么在这种情况下,如果服务器没有尝试发送任何内容,您可能不会收到任何内容; 如果这对于检测很重要,您可以定期发送一些数据,或者使用setsockopt()设置SO_KEEPALIVE套接字选项,以强制它在长时间(小时)不活动后发送没有数据的数据包。 如果没有收到确认,则recv()将返回-1,并且errno为ETIMEDOUT ,如果有更多特定信息,则返回另一个错误。

此外,如果您尝试在已断开连接的套接字上发送数据,则默认情况下SIGPIPE信号将终止您的程序。 通过将SIGPIPE信号操作设置为SIG_IGN(忽略),或者在支持它的系统(Linux)上使用带有MSG_NOSIGNAL标志的send()MSG_NOSIGNAL这种情况。