【问题标题】:write to TCP socket keeps returning EAGAIN写入 TCP 套接字不断返回 EAGAIN
【发布时间】:2016-08-01 01:51:06
【问题描述】:

我看到一种情况,即我有一个正在写入的 TCP 套接字(非阻塞),但在负载下它会陷入不断返回 EAGAIN 的情况。我知道这一点是因为它会停止处理,我可以附加一个调试器并单步执行它。逐步执行,write 调用返回一个错误,并且每次都将 errno 设置为 EAGAIN(它忙于等待 EAGAIN...忽略这是一个坏主意 :)

我的理解是只有在缓冲区已满时才应在写入时返回 EAGAIN,但我不明白什么会阻止它耗尽并且写入调用最终成功。

这是 Ubuntu,Linux 内核 3.19.0-47-generic。

想法?

【问题讨论】:

    标签: linux sockets networking tcp


    【解决方案1】:

    阻止它耗尽的原因是对等方的阅读速度不如您的写作速度。对方的接收缓冲区满了,你的发送缓冲区满了,你不能写了。

    它正忙着等待EAGAIN...忽略这是一个坏主意

    我不能忽视这一点。这是个坏主意。在这种特定情况下,您应该使用select() 来告诉您套接字何时变为可写,而不是盲目地循环。

    【讨论】:

    • 很多人认为 select 只是用于阅读。但您应该将其用于读取、写入和接受调用。如果你写得比你的读者快,你应该期望获得成功和 EAGAIN 的混合。如果你永远不能写任何东西,那么要么你的读者已经停止,要么有其他事情发生
    • @xaxxon 在这种情况下,您应该将它用于send() 调用。如果你永远无法写任何东西,要么你的阅读器已经停止,要么什么都没有:没有“其他东西”可能导致它最终不会导致连接超时。
    • 哎呀,我的意思是发送/接收/接受。你完全正确。
    • 所以,有趣的行为:通道的另一端正在使用阻塞套接字。事实证明它也在尝试写入,并且写入被阻塞。有趣的僵局。感谢您的回复!
    【解决方案2】:

    EAGAIN OR EWOULDBLOCK 在套接字处于非阻塞模式时返回。

    您可能正在通过调用fnctlO_NONBLOCK 标志(或类似效果)在某处初始化您的套接字。或者您正在使用 MSG_DONTWAIT 标志和 send 调用。

    无论如何,如果您不想将套接字转换回阻塞模式,只需在循环中调用发送就足够了。无论套接字是如何初始化的(阻塞或非阻塞),对于send 表示仅处理部分缓冲区的情况,我始终建议使用循环。

    int remaining = <bytes to send>;
    int sent = 0;
    const char* buffer = <data buffer to send>;
    int success = 0;
    
    while (remaining > 0  &&  !DoesTheAppNeedToExit())
    {
         int result = send(s, buffer+sent, remaining, 0);
         if (result != -1)
         {
             // sent partial or all the remaining data
             sent += result;
             remaining -= result;
             if (remaining <= 0)
             {
                 success = 1;
                 break;
             }
         }
         else
         {
           int err = errno;
           if ((err == EAGAIN) || (err == EWOULDBLOCK))
           {
               timeval tv = {};
               fd_set fds = {};
               int selectresult;
    
               tv.tv_sec = 1; // or how ever long you want to wait
               FD_ZERO(&fds);
               FD_SET(0, &fds);
               selectresult = select(s+1, NULL, &fds, NULL, &tv);
               // recommended: check return value from select and check for fatal error. Or timeout handling.
    
               // socket is ready if IS_FDSET(s, &fds), but it doesn't hurt to just try the send again at this point
           }
           else
           {
               // unrecoverable error!
               close(s);
               s = -1;
               break;
           }
         }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-18
      • 2013-05-25
      • 2011-01-30
      • 2014-02-03
      • 1970-01-01
      • 2015-08-04
      • 2018-02-03
      • 1970-01-01
      相关资源
      最近更新 更多