我看到的情况是我正在写一个TCP套接字(非阻塞),但在加载时会遇到不断返回EAGAIN的情况。我知道这是因为它停止了处理,我可以附加一个调试器并逐步完成它。单步执行,写入调用返回错误,并且每次都将errno设置为EAGAIN(它在EAGAIN上忙等待...忽略这是一个坏主意:)
我的理解是,如果缓冲区已满,EAGAIN只应在写入时返回,但我不明白什么会阻止它耗尽,写入调用最终会成功。
这是Ubuntu,Linux内核3.19.0-47-generic。
想法?
答案 0 :(得分:3)
阻止它耗尽的原因是同伴的阅读速度和你写作一样快。对等体的接收缓冲区填充,您的发送缓冲区填满,您无法写入。
忙于等待EAGAIN ......忽略这是一个坏主意
我无法忽视这一点。这是个坏主意。您应该在这种特定情况下使用select()
告诉您套接字何时变为可写,而不是盲目地循环。
答案 1 :(得分:0)
EAGAIN
当套接字处于非阻塞模式时,将返回OR EWOULDBLOCK
。
您可能正在通过调用fnctl
和O_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;
}
}
}