发送很多后,我的send()调用导致我的程序完全停顿。 这怎么可能?

所以基本上我正在使用C ++运行在Linux上的MMO服务器。 它起初工作正常,但在50个客户端40秒后,它将完全停顿。 当我debugging它时,我发现基本上它停止响应之前的最后一帧是syscall(),在此时它消失在内核中。 一旦它消失到内核中,它甚至不会返回一个值…这完全是莫名其妙的。

50个客户端每250毫秒发送23个字节。 这23个字节然后被广播给所有其他49个客户端。 这个过程开始减慢,然后最终完全停止,内核永远不会从send()命令的系统调用中返回。 这里有什么可能的原因? 这真是让我疯狂!

我发现的一个选项是强制延迟的Naglesalgorithm。 我试过切换它,但它仍然发生。

编辑:程序卡在这里。 具体来说,在发送中,这又会调用syscall()

bool EpollManager::s_send(int curFD, unsigned char buf[], int bufLen, int flag) // Meant to counteract partial sends { int sendRetVal = 0; int bytesSent = 0; while(bytesSent != bufLen) { print_buffer(buf, bufLen); sendRetVal = send(curFD, buf + bytesSent, bufLen - bytesSent, flag); cout << sendRetVal << " "; if(sendRetVal == -1) { perror("Sending failed"); return false; } else bytesSent += sendRetVal; } return true; } 

这也是调用s_send的方法。

  void EpollManager::broadcast(unsigned char msg[], int bytesRead, int sender) { for(iMap = connections.begin(); iMap != connections.end(); iMap++) { if(sender != iMap->first) { if(s_send(iMap->first, msg, bytesRead, 0)) // MSG_NOSIGNAL { if(debug) { print_buffer(msg, bytesRead); cout << "sent on file descriptor " << iMap->first << '\n'; } } } } if(connections.find(sender) != connections.end()) connections[sender]->reset_batch(); } 

并澄清连接是boost的unordered_map的一个实例。 程序呛的数据也不是唯一的。 它已被成功地广播到其他文件描述符,但至less看起来是随机的。

Solutions Collecting From Web of "发送很多后,我的send()调用导致我的程序完全停顿。 这怎么可能?"

内核保留一个有限的缓冲区来发送数据。 如果接收器没有收到,那个缓冲区将被填满,发送者将被阻塞。 这可能是问题吗?

TCP拥塞控制(即Nagle算法)与完整缓冲区( SO_SNDBUF套接字选项)一起将导致send()和类似的操作被阻塞。

这种懒惰的方式是为每个套接字实现单独的线程,但是这并不能太大。 在Linux上,您应该使用poll()或类似的非阻塞套接字,在Windows中您将调查IO完成端口。 看看中间件库来简化这一点, libevent是最近包含Windows IOCP支持的流行的跨平台示例,或者Boost:用于C ++的ASIO 。

一个有用的文章来读取IO可伸缩性将是C10K的问题 。

请注意,您真的不想禁用Nagle的Internet流量,即使在局域网中,您也可能会遇到主要问题,而没有任何形式的拥塞反馈。