【问题标题】:Client/server chatroom: Handle unexpected disconnect客户端/服务器聊天室:处理意外断开连接
【发布时间】:2015-02-21 15:00:30
【问题描述】:

我用 C 写了一个服务器 - 客户端聊天室。

服务器为与客户端的每个新连接创建一个新的 pthread,此 pthread 等待消息接收,并将此消息发送给所有其他客户端(使用所有文件描述符的数组)。如果客户端想要退出,他会通知服务器,他将终止 pthread 并从数组中删除文件描述符

这很好用!但是:

如果客户端意外断开连接,例如通过关闭终端,服务器不会从数组中删除文件描述符,而当其他客户端想要发送消息时,我会遇到错误,因为 pthread 试图发送消息到一个不再是连接的fd

现在我的问题:

如何在我发送消息之前测试客户端套接字的文件描述符是否仍然处于活动状态?

我的代码部分(来自 pthread):

for(i=0; i<*p_Nbr_Clients; i++){ // send the message to all the other clients
        if (fd_array[i] != fd){ // <- i want to test the fd here
                 if ( send(fd_array[i], msg, strlen(msg), 0) == -1 ){
                     perror("Serveur: send");
                 }
        }
}

【问题讨论】:

  • 您能否发布或说明您是如何测试哪个客户端已将消息发送到服务器的?
  • @Chandru 每个客户端在服务器上都有自己的线程,一个线程只关心一个客户端,线程知道他客户端的fd
  • 你应该在 for 循环之外缓存 strlen(msg) 的返回值,因为它会为每个参与者遍历 msg 数组,这是相当低效的。

标签: c sockets server file-descriptor


【解决方案1】:

检查recv() 的返回值。

如果用户异常终止,则返回值应为零0。

基于此,您可以轻松关闭 fd。

if(recv(fd,buffer,length,flag) == 0)
      close(fd);

【讨论】:

    【解决方案2】:

    没有独立的api来检查socket是否关闭。只需尝试将数据发送到该套接字。

    send 如果你写入一个关闭的套接字,将返回 -1。和errno 将被适当地设置。我猜你可能会得到EBADF 或ECONNRESET。检查 (Check connection open or closed ?(in C in Linux)) 和 (How to find out if a socket is closed)

    for(i=0; i<*p_Nbr_Clients; i++){ // send the message to all the other clients
            if (fd_array[i] != fd){ // <- i want to test the fd here
                     if ( send(fd_array[i], msg, strlen(msg), 0) == -1 ){
                         //perror("Serveur: send");
                         // something wrong, check errno to see more detail
                         // you need to include <errno.h> to use errno
                         close(fd_array[i]);
                         fd_array[i] = -1;// or something you define as not open
                     }
            }
    }
    

    【讨论】:

    • 所以我应该尝试一下{send} catch ...吗?因为错误会破坏服务器应用程序
    • C 中没有 try catch。我在答案中添加了一些注释,检查一下。
    • 如果套接字在本地是close()ed,您可能会得到EBADF。如果它已被远程关闭(这是 OP 的问题),您将得到 EPIPE 甚至是 SIGPIPE,默认行为是结束进程。
    • @alk。你只是颠倒了这两个。来自man send:EPIPE The local end has been shut down on a connection oriented socket
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多