【发布时间】:2020-01-08 20:23:02
【问题描述】:
前几天我在 epoll 上苦苦挣扎,现在我正处于茫茫人海中;)
互联网上有很多信息,显然是系统人,但我可能服用过量并有点困惑。
在我的服务器应用程序(nginx 的后端)中,我正在 ET 模式下等待来自客户端的数据:
event_template.events = EPOLLIN | EPOLLRDHUP | EPOLLET
当我注意到 nginx 以 502 响应时,一切都变得好奇了,尽管我可以看到我这边成功的 send()。我运行wireshark 嗅探并意识到我的服务器将(尝试并获取 RST)数据发送到网络上的另一台机器。所以,我决定套接字描述符是无效的,这是一种“未定义的行为”。最后,我发现在第二个 recv() 上,我得到零字节,这意味着必须关闭连接,并且不允许我再发回数据。尽管如此,我从 epoll 中获得的不仅仅是 EPOLLIN,而是 EPOLLRDHUP。
问题:在 EPOLLRDHUP 处理过程中,当 recv() 返回零和 shutdown(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 个并发写入是数据竞争,我必须处理的所有事情都是互斥体。
感谢所有至少读到最后的人:)
【问题讨论】:
-
您需要关闭任何零,而不仅仅是第一个。
-
毫无疑问“如果客户不等这么久怎么办?”如果您在
send()上收到 EAGAIN,则表示客户端根本没有读取数据,并且 TCP 已全部备份。