所以基本上我是在用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的一个实例。程序扼流的数据也不是唯一的。它已经成功地广播到其他文件描述符,但是至少看似随机的扼流圈。
答案 0 :(得分:2)
TCP拥塞控制,即Nagle算法,以及完整缓冲区(SO_SNDBUF
套接字选项)将导致send()
和类似操作被阻止。
这种懒惰的方法是为每个套接字实现单独的线程,但这不会扩展太多。在Linux上,您应该使用poll()
或类似的非阻塞套接字,使用Windows可以调查IO完成端口。查看中间件库以简化这一过程,libevent是一个流行的跨平台示例,最近包含了Windows IOCP支持,或者Boost:ASIO用于C ++。
阅读有关IO可伸缩性的有用文章将是The C10K problem。
请注意,您确实不想在互联网流量上禁用Nagle,即使在局域网上,如果没有某种形式的拥塞反馈,您可能会看到重大问题。
答案 1 :(得分:1)
内核保留一个有限的缓冲区来发送数据。如果接收方未接收,则该缓冲区将填满,发送方将阻止。这可能是问题吗?