gpt4 book ai didi

c++ - Epoll零recv()和负(EAGAIN)send()

转载 作者:太空宇宙 更新时间:2023-11-04 09:58:58 26 4
gpt4 key购买 nike

过去几天,我在epoll上苦苦挣扎,现在处在茫茫荒野之中;)

互联网上有很多信息,很明显,系统人中有很多信息,但是我可能服用过量并且有些困惑。

在我的服务器应用程序(后端到nginx)中,我正在等待ET模式下来自客户端的数据:
event_template.events = EPOLLIN | EPOLLRDHUP | EPOLLET
当我注意到nginx响应502时,一切都变得很好奇,尽管我可以看到成功的send()在我这一边。我跑钢丝
嗅探并意识到我的服务器向网络上的另一台计算机发送(尝试并获取RST)数据。因此,我认为套接字描述符无效,这是一种“未定义的行为”。最后,我发现在第二个recv()上,我得到了零字节,这意味着必须关闭连接,并且不再允许将数据发送回去。但是,我连续从epoll获得的不仅是EPOLLIN,而且是EPOLLRDHUP。

问题:当在EPOLLRDHUP处理过程中recv()返回零且稍后关机(SHUT_WR)时,是否必须关闭套接字以进行读取?

简而言之,从套接字读取:

    std::array<char, BatchSize> batch;
ssize_t total_count = 0, count = 0;
do {
count = recv(_handle, batch.begin(), batch.size(), MSG_DONTWAIT);

if (0 == count && 0 == total_count) {
/// @??? Do I need to wait zero just on first iteration?
close();
return total_count;
} else if (count < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/// @??? Will be back with next EPOLLIN?!
break ;
}
_last_error = errno;
/// @brief just log the error
return 0;
}

if (count > 0) {
total_count += count;
/// DATA!
if (count < batch.size()) {
/// @??? Received less than requested - no sense to repeat recv, otherwise I need one more turn?!
return total_count;
}
}
} while (count > 0);

可能是我的普遍错误是试图在无效的套接字描述符上发送数据,以后发生的一切只是后果。但是,我继续挖掘;)问题的第二部分也是关于以MSG_DONTWAIT模式写入套接字的问题。

据我所知,send()可能还会返回-1和EAGAIN,这意味着我应该订阅EPOLLOUT并等待内核缓冲区将有足够的空闲时间来接收我的一些数据。这是正确的吗?但是,如果客户不等那么久怎么办?或者,我可以调用阻塞发送(无论如何,我正在不同的线程上发送),并确保由于setsockopt(SO_LINGER),我发送给内核的所有内容都会真正发送给同级?我要确认的最后一个猜测是:我可以同时读写,但是N> 1个并发写入是数据竞争,而我要处理的所有事情都是互斥体。

感谢每个至少读到最后的人:)

最佳答案

Questions: Do I have to close socket just for reading when recv() returns zero and shutdown(SHUT_WR) later on during EPOLLRDHUP processing?



不,没有特别的理由执行那有些复杂的 Action 序列。

0接收到 recv()返回值后,您知道该连接在网络层至少关闭了一半。您将不会再收到任何东西,我也不希望在边缘触发模式下运行的EPoll能够进一步宣传其阅读准备,但这本身并不需要任何特殊操作。如果写端保持打开状态(从本地 Angular 看),则可以继续使用 write()send(),尽管您将没有确认收到发送内容的机制。

您实际应该执行的操作取决于您假设的应用程序级别协议(protocol)或消息交换模式。如果您希望远程对等方在等待您的数据时关闭其端点的写端(连接到本地端点的读端),则一定要发送它期望的数据。否则,您可能应该只关闭整个连接并在 recv()通过返回 0发出文件结束信号时停止使用它。请注意,对描述符进行 close()编码会自动将其从注册它的所有Epoll兴趣集中删除,但前提是没有其他打开文件描述符引用相同的打开文件描述。

无论如何,直到您对套接字进行 close()为止,即使您无法通过它成功通信,它也仍然有效。在此之前,没有理由期望您尝试通过它发送的消息会到达除原始远程端点之外的任何其他地方。尝试发送可能会成功,或者即使数据永远不会到达远端,也可能会发送尝试,或者尝试可能会失败,并出现几种不同的错误之一。

            /// @??? Do I need to wait zero just on first iteration?


无论是否已接收到任何数据,都应对返回值0采取措施。不一定是相同的 Action ,但是您应该以一种方式安排一种或另一种方式以使其脱离EPoll兴趣集,这很可能是通过将其关闭来实现的。

                /// @??? Will be back with next EPOLLIN?!


如果 recv()失败并显示 EAGAINEWOULDBLOCK,则EPoll可能会在以后的调用中很好地表示已准备就绪。不过,下一个不必一定要这样做。

                /// @??? Received less than requested - no sense to repeat recv, otherwise I need one more turn?! 


收到比您要求少的钱是您应始终做好的准备。这并不一定意味着另一个 recv()不会返回任何数据,并且如果您在EPoll中使用边沿触发模式,则假定相反的做法很危险。在这种情况下,您应该继续以非阻塞模式或 recv()进入 MSG_DONTWAIT,直到使用 EAGAINEWOULDBLOCK调用失败。

As far as I now know, send() may also return -1 and EAGAIN which means that I'm supposed to subscribe on EPOLLOUT and wait when kernel buffer will be free enough to receive some data from my me. Is this right?


send()肯定会因 EAGAINEWOULDBLOCK而失败。它也可以成功,但是发送的字节数少于请求的字节数,您应该为此做好准备。无论哪种方式,通过在文件描述符上订阅EPOLLOUT事件进行响应都是合理的,以便稍后恢复发送。

But what if client won't wait so long?



这取决于客户在这种情况下的行为。如果关闭连接,则以后尝试对其进行 send()失败,并会出现其他错误。如果您仅在描述符上注册了EPOLLOUT事件,那么我怀疑是否有可能陷入这种尝试永远不会发生的情况,因为不会发出进一步的事件。即使您主要的兴趣是写作,也可以通过注册并正确处理 EPOLLRDHUP事件来进一步降低这种可能性。

如果客户端放弃而不关闭连接,那么 EPOLLRDHUP可能不会有用,并且您更有可能将陈旧的连接无限期地卡在EPoll中。可能需要通过每个FD超时来解决这种可能性。

Or, may I call blocking send(anyway, I'm sending on a different thread) and guarantee the everything what I send to kernel will be really sent to peer because of setsockopt(SO_LINGER)?



如果您有一个专门用于发送该特定文件描述符的单独线程,那么您当然可以考虑阻止 send()。唯一的缺点是您不能在此之上实现超时,但是除此之外,如果该线程在发送数据或接收更多要发送的数据时发生阻塞,该线程将做什么?

但是,至少在本地,我看不出 SO_LINGER与它有什么关系。内核将尽一切努力通过 send()调用将您已经分派(dispatch)的数据发送到远程对等设备,即使您在数据仍处于缓冲状态时对套接字进行 close()设置,无论 SO_LINGER的值如何。该选项的目的是在连接关闭后接收(并丢弃)与连接关联的散乱数据,以免将它们意外传送到另一个套接字。

但是,这些都不能保证数据已成功传递到远程对等方。没有什么可以保证的。

And a final guess which I ask to confirm: I'm allowed to read and write simultaneously, but N>1 concurrent writes is a data race and everything that I have to deal with it is a mutex.



套接字是全双工的,是的。而且,POSIX要求大多数功能(包括 send()recv())都是线程安全的。但是,多个线程写入同一套接字会带来麻烦,因为单个调用的线程安全性不能保证多个调用之间的一致性。

关于c++ - Epoll零recv()和负(EAGAIN)send(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57813838/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com