写入TCP套接字不断返回EAGAIN

时间:2016-04-11 03:54:59

标签: linux sockets networking tcp

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

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

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

想法?

2 个答案:

答案 0 :(得分:3)

阻止它耗尽的原因是同伴的阅读速度和你写作一样快。对等体的接收缓冲区填充,您的发送缓冲区填满,您无法写入。

  

忙于等待EAGAIN ......忽略这是一个坏主意

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

答案 1 :(得分:0)

EAGAIN当套接字处于非阻塞模式时,将返回OR EWOULDBLOCK

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

在任何情况下,如果您不想将套接字转换回阻止模式,只需在循环中调用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;
       }
     }
}