gpt4 book ai didi

c++ - 套接字选择死锁的可能原因

转载 作者:IT王子 更新时间:2023-10-29 00:42:12 31 4
gpt4 key购买 nike

我有一个 jabber 服务器应用程序和另一个用 C++ 编写的 jabber 客户端应用程序。

当客户端接收和发送大量消息(每秒超过 20 条)时,选择会卡住并且永远不会返回。

使用 netstat,socket 仍然连接在 linux 上,使用 tcpdump,消息仍然发送到客户端,但选择永远不会返回。

这里是选择的代码:

bool ConnectionTCPBase::dataAvailable( int timeout )
{
if( m_socket < 0 )
return true; // let recv() catch the closed fd

fd_set fds;
struct timeval tv;

FD_ZERO( &fds );
// the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
// however, the reason for the warning can't be fixed in gloox.
FD_SET( m_socket, &fds );

tv.tv_sec = timeout / 1000000;
tv.tv_usec = timeout % 1000000;

return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
&& FD_ISSET( m_socket, &fds ) != 0 );
}

死锁与 gdb 有关:

Thread 2 (Thread 0x7fe226ac2700 (LWP 10774)):
#0 0x00007fe224711ff3 in select () at ../sysdeps/unix/syscall-template.S:82
#1 0x00000000004706a9 in gloox::ConnectionTCPBase::dataAvailable (this=0xcaeb60, timeout=<value optimized out>) at connectiontcpbase.cpp:103
#2 0x000000000046c4cb in gloox::ConnectionTCPClient::recv (this=0xcaeb60, timeout=10) at connectiontcpclient.cpp:131
#3 0x0000000000471476 in gloox::ConnectionTLS::recv (this=0xd1a950, timeout=648813712) at connectiontls.cpp:89
#4 0x00000000004324cc in glooxd::C2S::recv (this=0xc5d120, timeout=10) at c2s.cpp:124
#5 0x0000000000435ced in glooxd::C2S::run (this=0xc5d120) at c2s.cpp:75
#6 0x000000000042d789 in CNetwork::run (this=0xc56df0) at src/Network.cpp:343
#7 0x000000000043115f in threading::ThreadManager::threadWorker (data=0xc56e10) at src/ThreadManager.cpp:15
#8 0x00007fe2249bc9ca in start_thread (arg=<value optimized out>) at pthread_create.c:300
#9 0x00007fe22471970d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#10 0x0000000000000000 in ?? ()

你知道什么会导致 select 停止接收消息,即使我们仍在向他发送消息。当通过套接字接收和发送大量消息时,linux 中是否有缓冲区限制?

谢谢

最佳答案

有几种可能性。

超过FD_SETSIZE

您的代码正在检查是否存在负文件描述符,但不会检查是否超过上限 FD_SETSIZE (通常为 1024)。每当发生这种情况时,您的代码就是

  • 破坏自己的堆栈
  • 呈现一个空的 fd_setselect这将导致挂起

假设您不需要那么多并发打开的文件描述符,解决方案可能包括找到一个消除文件描述符泄漏的方法,尤其是堆栈上处理废弃描述符关闭的代码。

您的代码中存在可疑注释,表明可能存在泄漏:

// let recv() catch the closed fd

如果这条评论意味着有人设置了m_socket到 -1 并希望 recv将捕获关闭的套接字并关闭它,谁知道呢,也许我们正在关闭 -1 而不是真正关闭的套接字。 (请注意网络级别关闭和文件描述符级别关闭之间的区别,后者需要单独的 close 调用。)

这也可以通过移至 poll 来解决。但是操作系统强加的一些其他限制使这条路线非常具有挑战性。

带外数据

你说服务器正在“发送”数据。如果这意味着数据是使用 send 发送的调用(而不是 write 调用),使用 strace确定发送标志参数。如果MSG_OOB使用标志,数据作为带外数据到达 - 而您的 select在您传递 fds 的拷贝之前,调用不会注意到这些作为另一个参数。

fd_set fds_copy = fds;
select( m_socket + 1, &fds, 0, &fds_copy, timeout == -1 ? 0 : &tv )

进程饥饿

如果盒子严重重载,服务器将在没有任何阻塞调用的情况下执行,并且具有实时优先级(使用 top 来检查) - 而客户端不是 - 客户端可能会饿死。

暂停进程

客户端理论上可能会被 SIGSTOP 停止.您可能知道是否是这种情况,按下某处 ctrl-Z 或让某个特定进程对客户端进行控制而不是您自己启动它。

关于c++ - 套接字选择死锁的可能原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11179040/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com