套接字选择中死锁的可能原因 我有一个JabBER服务器应用程序,另一个Jabbor客户端应用程序在C++中。

套接字选择中死锁的可能原因 我有一个JabBER服务器应用程序,另一个Jabbor客户端应用程序在C++中。,c++,linux,select,deadlock,gloox,C++,Linux,Select,Deadlock,Gloox,当客户端接收和发送大量消息(每秒超过20条)时,select将立即冻结,不再返回 使用netstat时,套接字仍在linux上连接,而使用tcpdump时,消息仍会发送到客户端,但select永远不会返回 以下是选择的代码: bool ConnectionTCPBase::dataAvailable( int timeout ) { if( m_socket < 0 ) return true; // let recv() catch the closed fd

当客户端接收和发送大量消息(每秒超过20条)时,select将立即冻结,不再返回

使用netstat时,套接字仍在linux上连接,而使用tcpdump时,消息仍会发送到客户端,但select永远不会返回

以下是选择的代码:

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 );
  }
bool connectioncpbase::dataAvailable(int超时)
{
如果(m_插座<0)
return true;//让recv()捕捉关闭的fd
fd_集fds;
结构时间值电视;
FD_零(&fds);
//以下内容在VC++Express 2008和其他版本中可能会导致C4127警告。
//但是,在gloox中无法修复警告的原因。
FD_套件(m_插座和fds);
tv.tv_sec=超时/1000000;
tv.tv_usec=超时%1000000;
返回((选择(m_套接字+1,&fds,0,0,超时==-1?0:&tv)>0)
&&FD_ISSET(m_插座和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 ?? ()
线程2(线程0x7fe226ac2700(LWP 10774)):
#0 0x00007fe224711ff3位于../sysdeps/unix/syscall template.S:82的select()中
#gloox::ConnectionCpBase::dataAvailable(this=0xcaeb60,timeout=)中的1 0x00000000004706a9位于ConnectionCpBase.cpp:103
#在ConnectionCpcClient.cpp:131处的gloox::ConnectionCpcClient::recv(this=0xcaeb60,timeout=10)中有2个0x000000000046c4cb
#在ConnectionTLS.cpp:89处的gloox::ConnectionTLS::recv(this=0xd1a950,timeout=648813712)中的3 0x0000000000471476
#在C2S处的GLOXD::C2S::recv(此=0xc5d120,超时=10)中有4 0x00000000004324C。cpp:124
#5 0x0000000000435ced在GLOXD::C2S::run(this=0xc5d120)中,在C2S处运行。cpp:75
#CNetwork::在src/Network.cpp:343处运行(此=0xc56df0)中的6 0x000000000042d789
#在src/ThreadManager.cpp:15处的threading::ThreadManager::threadWorker(数据=0xc56e10)中有7 0x000000000043115f
#在pthread_create.c:300的start_线程(arg=)中的8 0x00007fe2249bc9ca
#9 0x00007fe22471970d位于../sysdeps/unix/sysv/linux/x86_64/clone.S:112的clone()中
#10 0x0000000000000000英寸??()
您知道什么会导致select停止接收消息,即使我们仍在向他发送消息。 当通过套接字接收和发送大量消息时,linux中是否存在缓冲区限制


谢谢

有几种可能性

超过
FD\u设置大小

您的代码正在检查负文件描述符,但没有超过上限,即
FD_SETSIZE
(通常为1024)。无论何时发生这种情况,您的代码都是

  • 破坏自己的堆栈
  • 将空的
    fd_集
    呈现给
    选择
    ,这将导致挂起
假设您不需要这么多并发打开的文件描述符,解决方案可能包括找到一个方法来删除文件描述符泄漏,特别是堆栈中处理关闭废弃描述符的代码

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

// let recv() catch the closed fd
如果此注释意味着有人将
m_socket
设置为-1,并希望
recv
将捕获已关闭的套接字并将其关闭,谁知道呢,可能我们正在关闭-1而不是真正的已关闭套接字。(请注意在网络级别关闭与在文件描述符级别关闭之间的区别,这需要单独的
close
调用。)

这也可以通过移动到
poll
来解决,但是操作系统施加的一些其他限制使得此路线非常具有挑战性

带外数据

您说服务器正在“发送”数据。如果这意味着使用
send
调用发送数据(与
write
调用相反),请使用
strace
确定send flags参数。如果使用了
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键的某个位置,或者有某个特定的进程在客户端上执行控制,而不是您自己启动它。

可能是另一端崩溃或关闭了。我建议总是有一个非无限超时来选择
(例如,一秒钟超时)。我还建议使用
poll
not
select
。您可以
strace
您的程序,以了解
select
是如何“错误”运行的。另一端(服务器)仍在运行,并且仍在发送消息。使用tcpdump,我可以看到发送到客户端端口的消息。我们在select中不使用timeout的原因是,在没有发送任何内容的情况下,不会过度消耗cpu。另外,当我在客户机上运行strace时,他仍然在等待套接字。socket仍然可以在/proc//fd/“通过tcpdump,我可以看到发送到客户端端口的消息”。那么您是在服务器端运行tcpdump吗?如果您嗅探本地计算机上的流量呢?这些数据包是否到达?您至少可以检查select()的返回值,如果是-1,则检查errno。可能是EINTR/EAGAIN或EINVAL。。。服务器和客户端运行在同一台机器上。因此,客户端通过端口5222连接到服务器,客户端端口类似于41919。因此,当我在接口“lo”上运行tcpdump时,我可以看到一个数据包已经从5222发送到端口41919。谢谢您的回答。客户机应用程序没有饿死。以及