2010-04-07 48 views
9

想,我有一个连接插座编写这些代码以后。如何知道客户在插座已终止

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

我的客户一边是这个(马爷是无关紧要的,而回答我的问题)

#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

你可以包含更多的代码吗?这是什么样的插座?服务器端是什么意思? – WhirlWind 2010-04-07 14:31:43

+0

我已添加完整的服务器端代码。我刚开始学习套接字编程,所以服务器端不是很强大......欢迎任何建议。 – 2010-04-07 14:57:42

回答

12

接受插座上的连接,你的recv()将返回0或-1在特殊情况下经过。从recv(3) man page

摘录:

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

因此,如果您的客户端优雅地退出,您将在某个时刻从recv()得到0。如果连接丢失了,你也可以得到-1,检查合适的errno会告诉你连接是否丢失了一些其他的错误。在recv(3) man page查看更多详情。

编辑:

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

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

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

+0

不,只有当服务器端关闭了套接字。 – WhirlWind 2010-04-07 14:32:54

+0

怎么回事?在接受()'从其他套接字读取的套接字之后。 – pajton 2010-04-07 14:35:15

+0

当*写入*时,请注意'errno == EPIPE'。 – 2010-04-07 15:16:19

8

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

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

+0

+1给出更多特殊情况的详细解释。 – pajton 2010-04-07 16:13:24

+0

即使这是相当古老的,解释为什么我的进程终止,如果一个客户端断开刚刚救了我的一天!非常感谢你! – ZeroTek 2016-11-26 11:05:27

相关问题