客户端断开连接时套接字服务器崩溃
Posted
技术标签:
【中文标题】客户端断开连接时套接字服务器崩溃【英文标题】:Socket server crashes while client has disconnected 【发布时间】:2021-12-28 14:04:51 【问题描述】:练习 C 语言和套接字编程。
服务器代码在检查实时客户端并且客户端已断开连接(例如连接断开)时崩溃。我没有阅读 GDB 转储的经验。有人可以指出我在这里缺少什么吗?
这是服务器代码。客户端连接和断开连接。服务器通过发送一条小消息并等待回复来检查断开连接的客户端。收到损坏的管道错误后,代码崩溃。
void * client_hartbeat()
int ret = 0, i;
char send_msg[50] = "e";
char recv_msg[50];
while(1)
sleep(3);
printf("\nchecking for disconnected clients\n");
memset(&recv_msg,0,sizeof(recv_msg));
for(i=0;i<CLIENTS;i++)
if(client_pool[i]!=0)
printf("TEST0, socket: %i thread: %i\n", client_pool[i],pthread_self());
if(send(client_pool[i],send_msg,sizeof(send_msg),0) < 0)
printf("send error: %s\n", strerror(errno));
else if(recv(client_pool[i],recv_msg,sizeof(recv_msg),0) < 0)
printf("receive error: %s\n", strerror(errno));
printf("TEST1, socket: %i thread: %d\n", client_pool[i],pthread_self());
printf("TEST1\n");
pthread_exit(&th2);
这是 GDB 转储:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
waiting connections...
[New Thread 0x7ffff7dba640 (LWP 22567)]
[New Thread 0x7ffff75b9640 (LWP 22568)]
checking for disconnected clients
checking for disconnected clients
TEST0, socket: 4 thread: -144992704
TEST1, socket: 4 thread: -144992704
checking for disconnected clients
TEST0, socket: 4 thread: -144992704
TEST1, socket: 4 thread: -144992704
checking for disconnected clients
TEST0, socket: 4 thread: -144992704
Thread 3 "a.out" received signal SIGPIPE, Broken pipe.
[Switching to Thread 0x7ffff75b9640 (LWP 22568)]
__libc_send (flags=<optimized out>, len=50, buf=0x7ffff75b8e10, fd=4) at ../sysdeps/unix/sysv/linux/send.c:28
28 ../sysdeps/unix/sysv/linux/send.c: No such file or directory.
(gdb)
【问题讨论】:
您可能应该忽略该信号。signal(SIGPIPE, SIG_IGN);
。无关:你发送sizeof(send_msg)
(50字节)即使我怀疑你只发送1字节(e
)。
我之前确实尝试过 1 个字节,也尝试过 strlen(send_msg) 而不是 sizeof(send_msg) 并且仍然代码崩溃
是的,那是一条不相关的评论。您是否尝试忽略信号?如果您尝试在关闭的套接字上使用 send
/recv
,您还可以在 send
和 recv
调用中使用标志 MSG_NOSIGNAL
以不生成信号。
我用我的建议写了一个答案
好主意,刚刚尝试使用 MSG_NOSIGNAL 现在服务器代码没有崩溃,从服务器的角度来看,客户端仍然连接,不幸的是,这并不能解决这个问题。服务器应该知道客户端已断开连接。
【参考方案1】:
这些选项中的任何一个都应该可以避免SIGPIPE
信号杀死您的程序:
SIGPIPE
:
#include <signal.h>
int main()
signal(SIGPIPE, SIG_IGN);
...
当send
/recv
尝试通过提供MSG_NOSIGNAL
标志对关闭的套接字进行操作时,不要生成SIGPIPE
信号:
if (send(client_pool[i], send_msg, strlen(send_msg), MSG_NOSIGNAL) < 0)
printf("send error: %s\n", strerror(errno));
else if (recv(client_pool[i], recv_msg, sizeof(recv_msg), MSG_NOSIGNAL) < 0)
printf("receive error: %s\n", strerror(errno));
来自send
man
页面:
MSG_NOSIGNAL
(Linux 2.2 起)
如果面向流的套接字上的对等方已关闭连接,则不要生成SIGPIPE
信号。仍然返回 EPIPE
错误。这提供了与使用sigaction(2)
来忽略SIGPIPE
类似的行为,但是,虽然MSG_NOSIGNAL
是一个每次调用的功能,但忽略SIGPIPE
会设置一个影响进程中所有线程的进程属性。 p>
您还需要在使用send
后检测套接字的状态,因为它可能会在客户端死亡后的第一个send
上返回成功。
你可以添加一个辅助函数:
int checksock(int sock) // 0 on success, -1 on failure
int error = 0;
socklen_t len = sizeof error;
int rv = getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
if (rv == 0 && error)
errno = error; // set the thread local errno
rv = -1;
return rv;
然后您的发送部分可以这样做:
if (send(client_pool[i], send_msg, strlen(send_msg), MSG_NOSIGNAL) < 0
|| checksock(client_pool[i])) // added check
printf("send error: %s\n", strerror(errno)); // Broken pipe
else if (recv(client_pool[i], recv_msg, sizeof(recv_msg), MSG_NOSIGNAL) < 0)
printf("receive error: %s\n", strerror(errno));
【讨论】:
检查SO_ERROR
通常只能在非阻塞操作之后进行(例如当connect()
返回EINPROGRESS
/EWOULDBLOCK
然后select()
报告套接字是可写的,即@ 987654347@操作完成,然后使用SO_ERROR
查看socket是否真正连接)。否则,在检查返回值是否失败后,请改用errno
。
@RemyLebeau 不同之处在于,由于send
在客户端死亡后的第一个send
上返回成功,所以errno
是0
。另一方面,用getsockopt
检查SO_ERROR
会返回错误,因此可以在第一个send
上检测到断开连接。
如果send()
“成功”,则无法保证SO_ERROR
会更新为错误代码,这样做也没有任何意义。
@RemyLebeau 不,我不知道为什么它在实践中似乎有效,但我阅读了不止一个建议来检查SO_ERROR
(以及打开keepalive)。在我做的非常小的测试中,它在第一次send
之后从未检测到它,但在仅检查来自send
的返回时它一直未能检测到它。也许getsockopt
和SO_ERROR
不完全是被动的?以上是关于客户端断开连接时套接字服务器崩溃的主要内容,如果未能解决你的问题,请参考以下文章
如何处理与 python 套接字的断开连接? (连接重置错误)
为啥当客户端断开连接时这个简单的 websocket 代码会抛出?