C++ Epoll zero recv()和negative(EAGAIN)send() 我在埃博里挣扎了几天,现在我身处偏僻的地方;p>
互联网上有很多信息,很明显系统中也有很多信息,但我可能服用过量,有点困惑 在我的服务器应用程序(nginx后端)中,我正在ET模式下等待来自客户端的数据:C++ Epoll zero recv()和negative(EAGAIN)send() 我在埃博里挣扎了几天,现在我身处偏僻的地方;p>,c++,linux,epoll,epollet,C++,Linux,Epoll,Epollet,互联网上有很多信息,很明显系统中也有很多信息,但我可能服用过量,有点困惑 在我的服务器应用程序(nginx后端)中,我正在ET模式下等待来自客户端的数据: event_template.events=EPOLLIN | EPOLLRDHUP | EPOLLET 当我注意到nginx用502响应时,一切都变得很奇怪,尽管我可以看到成功的send()。我经营wireshark 嗅探并意识到我的服务器向网络上的另一台机器发送(尝试并获取RST)数据。所以,我决定套接字描述符无效,这是一种“未定义的行为
event_template.events=EPOLLIN | EPOLLRDHUP | EPOLLET
当我注意到nginx用502响应时,一切都变得很奇怪,尽管我可以看到成功的send()。我经营wireshark
嗅探并意识到我的服务器向网络上的另一台机器发送(尝试并获取RST)数据。所以,我决定套接字描述符无效,这是一种“未定义的行为”。最后,我发现在第二个recv()上,我得到了零字节,这意味着连接必须关闭,我不能再发送数据了。然而,我从epoll得到的不仅仅是EPOLLIN,还有EpollHup
问题:当recv()返回零并在稍后的EPOLLRDHUP处理过程中关闭(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);
std::数组批处理;
总计数=0,计数=0;
做{
count=recv(_handle,batch.begin(),batch.size(),MSG_DONTWAIT);
如果(0==计数和&0==总计数){
///@???我需要在第一次迭代时等待零吗?
close();
返回总计数;
}否则如果(计数<0){
if(errno==EAGAIN | | errno==ewoldblock){
///@???会和下一个EPOLLIN一起回来吗?!
打破
}
_最后一个错误=errno;
///@brief只需记录错误
返回0;
}
如果(计数>0){
总计数+=计数;
///数据!
如果(计数0);
我的错误可能是试图在无效的套接字描述符上发送数据,而随后发生的一切都只是一个结果。但是,我继续挖掘;)我的问题的第二部分是关于在MSG_DONTWAIT模式下写入套接字
就我现在所知,send()还可能返回-1和EAGAIN,这意味着我应该订阅ePlolout并等待内核缓冲区足够空闲时从我的me接收一些数据。是这样吗?但如果客户不愿意等这么久呢?或者,我可以调用blocking send(无论如何,我是在另一个线程上发送的)并保证我发送给内核的所有内容都会因为setsockopt而真正发送给peer(所以_LINGER)?我想确认的最后一个猜测是:我可以同时读写,但N>1的并发写操作是一种数据竞争,我必须处理的所有事情都是互斥
感谢所有至少读到最后的人:)
问题:当recv()时,我是否必须关闭插座以进行读取
返回零并在EPOLLRDHUP期间稍后关闭(SHUT_WR)
处理
不,没有特别的理由去执行那个有点复杂的动作序列
从recv()
接收到0
返回值后,您知道网络层的连接至少关闭了一半。您将不会从它那里收到任何进一步的信息,我也不希望在边缘触发模式下运行的EPoll进一步宣传它已准备好阅读,但这本身并不需要任何特定的操作。如果写入端保持打开状态(从本地角度),则您可以继续在其上执行write()
或send()
,尽管您将没有确认收到发送内容的机制
您实际应该做什么取决于您假设的应用程序级协议或消息交换模式。如果您希望远程对等机在等待您的数据时关闭其端点的写入端(连接到本地端点的读取端),那么请务必发送它预期的数据。否则,当recv()
返回0
表示文件结束时,您可能应该关闭整个连接并停止使用它。请注意,close()
ing描述符将自动从其注册的任何Epoll兴趣集中删除它,但前提是没有其他打开文件描述符引用相同的打开文件描述
无论如何,在您执行close()
套接字之前,它仍然有效,即使您无法通过它成功通信。在此之前,没有理由期望您试图通过它发送的消息会到达原始远程端点之外的任何地方。发送的尝试可能成功,或者即使数据从未到达远端,也可能看似成功,或者发送可能会因几个不同错误之一而失败
无论是否已收到任何数据,都应该对返回值0采取操作。不一定是相同的操作,但无论哪种方式,您都应该安排一种或另一种方式将其从EPoll兴趣集中取出,很可能是通过关闭它
如果recv()
因EAGAIN
或eWoldBlock
而失败,则EPoll很可能会在将来的调用中发出读取准备就绪的信号。不过,不一定是下一个
收到比你要求的少的东西是一种可能性,你应该随时准备。这并不一定意味着另一个recv()
不会返回任何数据,如果您在EPoll中使用边缘触发模式,则假设相反是危险的。在这种情况下,您应该在非阻塞模式下或使用MSG\u DONTWAIT继续执行recv()
/// @??? Do I need to wait zero just on first iteration?
/// @??? Will be back with next EPOLLIN?!
/// @??? Received less than requested - no sense to repeat recv, otherwise I need one more turn?!