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

时间:2010-12-31 05:53:08

标签: c++ linux tcp linux-kernel send

所以基本上我是在用Linux运行的C ++中制作一个MMO服务器。它起初工作正常,但在50个客户端可能40秒之后它将完全暂停。当我调试它时,我发现基本上它停止响应之前的最后一帧是syscall(),此时它会消失在内核中。一旦它消失在内核中,它甚至都不会返回一个值...它完全令人费解。

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

我找到的一个选项是强制延迟的Nagles算法。我试过切换它但是它仍然会发生。

编辑:程序卡在这里。具体来说,在send中,它又调用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的一个实例。程序扼流的数据也不是唯一的。它已经成功地广播到其他文件描述符,但是至少看似随机的扼流圈。

2 个答案:

答案 0 :(得分:2)

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

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

阅读有关IO可伸缩性的有用文章将是The C10K problem

请注意,您确实不想在互联网流量上禁用Nagle,即使在局域网上,如果没有某种形式的拥塞反馈,您可能会看到重大问题。

答案 1 :(得分:1)

内核保留一个有限的缓冲区来发送数据。如果接收方未接收,则该缓冲区将填满,发送方将阻止。这可能是问题吗?