写入TCP套接字一直返回EAGAIN。

4
我看到一个情况,我有一个TCP套接字(非阻塞),正在向其中写入数据,但在负载高的情况下,它会持续返回EAGAIN。我知道这是因为它停止了处理,我可以附加调试器并逐步执行它。通过逐步执行,每次写入调用都返回错误,并且errno被设置为EAGAIN(它在EAGAIN上忙等待...请忽略这是一个坏主意 :))。
我的理解是,只有在缓冲区已满时,才应该在写入时返回EAGAIN,但我不明白是什么导致它无法排空缓冲区,最终使写入调用成功。
这是Ubuntu,Linux内核3.19.0-47-generic。
有什么想法吗?
2个回答

4

阻止其排空的原因是对等方的读取速度不如您的写入速度。对等方的接收缓冲区填满,您的发送缓冲区也填满了,您无法进行写入。

它在等待 EAGAIN 上的 busy-waits...忽略这是一个坏主意

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


1
很多人认为select只是用于读取..但你应该用它来进行读、写和接受调用。如果你的写入速度比读取速度快,你应该期望得到一些成功和EAGAINs的混合..如果你从未能够写入任何内容,那么要么你的读取器已经停止工作,要么还有其他问题。 - xaxxon
@xaxxon 在这种情况下,你应该仅将其用于 send() 调用。如果你永远无法写入任何内容,那么要么你的读取器已停止,要么就是没有任何东西:没有其他可能导致它的 '其他原因' 最终都会超时连接。 - user207421
哎呀,我的意思是发送/接收/接受。你完全正确。 - xaxxon
2
所以,有趣的行为:通道的另一端正在使用阻塞套接字。结果发现它也在尝试写入,并且写入是阻塞的。有趣的死锁。感谢回复! - rivenmyst137
1
所以,有趣的行为:通道的另一端正在使用阻塞套接字。结果它也在尝试写入,而写入是阻塞的。有趣的死锁。这真的可能吗?我从未听说过。 - avernus

0

当套接字处于非阻塞模式时,会返回EAGAINEWOULDBLOCK

你可能在某个地方使用了fnctl调用和O_NONBLOCK标志(或类似的效果)来初始化套接字。或者你正在使用send调用的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;
       }
     }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接