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

Posted

技术标签:

【中文标题】在发送了很多之后,我的 send() 调用导致我的程序完全停止。这怎么可能?【英文标题】:After sending a lot, my send() call causes my program to stall completely. How is this possible? 【发布时间】:2011-06-01 21:19:52 【问题描述】:

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

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

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

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

【问题讨论】:

如果您正在执行阻塞发送,您必须确保在另一端正在读取某些内容以解除对发件人的阻塞。 【参考方案1】:

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

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

关于 IO 可扩展性的有用文章是 The C10K problem。

请注意,您确实不想在 Internet 流量上禁用 Nagle,即使在 LAN 上,您也可能会在没有某种形式的拥塞反馈的情况下看到重大问题。

【讨论】:

我目前正在使用带有阻塞套接字的 epoll。缓冲区似乎已满。【参考方案2】:

内核为发送数据保留了一个有限的缓冲区。如果接收方没有接收,该缓冲区将填满,发送方将阻塞。会不会是这个问题?

【讨论】:

以上是关于在发送了很多之后,我的 send() 调用导致我的程序完全停止。这怎么可能?的主要内容,如果未能解决你的问题,请参考以下文章

SMS_SEND 权限导致编译错误并警告防病毒

MPI_SEND 占用大量虚拟内存

AJAX xmlhttp.send 参数

kafka生产者投递消息特征(异步发送)

信号中断阻塞模式下的发送方法

哪个是为智能合约方法调用 send() 的最佳方法?