也请参见this question,目前尚未答复。
即使在EPOLLHUP
和Kernel文档中,对man
也有很多困惑。人们似乎认为,当轮询在本地关闭以进行写入的描述符(即shutdown(SHUT_WR)
,即在同位体上导致EPOLLRDHUP
的同一调用时,它会返回。 / em>。但这是不正确的,在我的实验中,EPOLLOUT
之后我得到EPOLLHUP
,而没有shutdown(SHUT_WR)
(是的,写作可写是违反直觉的,一半是封闭的,但这不是问题的重点。
man很差,因为它说EPOLLHUP
是在关联文件描述符上发生挂断时出现的,EPOLLHUP
却没有说“挂断”是什么意思-同行吗?发送了什么数据包? This other article只是使事情更加混乱,对我来说似乎是完全错误的。
我的实验表明,shutdown(SHUT_WR)
是在双方交换EOF(FIN数据包)后即到达双方SHUT_RD
后到达的。它与close
无关,我从未称呼它。也与EPOLLHUP
无关。在数据包方面,我怀疑TcpSocket::createListener
是在主机发送的FIN的确认信号上引发的,即终止发起方在4向关机握手的第3步中引发此事件,而对等方在步骤4(请参阅here)。如果得到确认,那就太好了,因为它填补了我一直在寻找的空白,即如何在不使用LINGER的情况下轮询非阻塞套接字以获得最终的确认。 这正确吗?
(注意:我正在使用ET,但我认为与此无关)
代码在框架中,我提取了其中的内容,但TcpSocket::connect
,TcpSocket::accept
和void registerFd(int pollFd, int fd, const char* description)
{
epoll_event ev = {
EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET,
const_cast<char*>(description) // union aggregate initialisation, initialises first member (void* ptr)
};
epoll_ctl(pollFd, EPOLL_CTL_ADD, fd, &ev);
}
struct EventPrinter
{
friend std::ostream& operator<<(std::ostream& stream, const EventPrinter& obj)
{
return stream << "0x" << std::hex << obj.events_ << " = "
<< ((obj.events_& EPOLLIN) ? "EPOLLIN " : " ")
<< ((obj.events_& EPOLLOUT) ? "EPOLLOUT " : " ")
<< ((obj.events_& EPOLLERR) ? "EPOLLERR " : " ")
<< ((obj.events_& EPOLLRDHUP) ? "EPOLLRDHUP " : " ")
<< ((obj.events_& EPOLLHUP) ? "EPOLLHUP " : " ");
}
const uint32_t events_;
};
void processEvents(int pollFd)
{
static int iterationCount = 0;
++iterationCount;
std::array<epoll_event, 25> events;
int eventCount;
if (-1 ==
(eventCount = epoll_wait(pollFd, events.data(), events.size(), 1)))
{
throw Exception("fatal: epoll_wait failed");
}
for (int i = 0; i < eventCount; ++i)
{
std::cout << "iteration #" << iterationCount << ": events on [" << static_cast<const char*>(events[i].data.ptr) << "]: [" << EventPrinter{events[i].events} << "]" << std::endl;
}
}
TEST(EpollhupExample, SmokeTest)
{
int pollFd_;
if (-1 ==
(pollFd_ = epoll_create1(0)))
{
throw Exception("fatal: could not create epoll socket");
}
const TcpSocket listener_ = TcpSocket::createListener(13500);
if (!listener_.setFileStatusFlag(O_NONBLOCK, true))
throw Exception("could not make listener socket non-blocking");
registerFd(pollFd_, listener_.fd(), "listenerFD");
const TcpSocket client = TcpSocket::connect("127.0.0.1", AF_INET, 13500);
if (!client.valid()) throw;
registerFd(pollFd_, client.fd(), "clientFD");
//////////////////////////////////////////////
/// start event processing ///////////////////
//////////////////////////////////////////////
processEvents(pollFd_); // iteration 1
const TcpSocket conn = listener_.accept();
if (!conn.valid()) throw;
registerFd(pollFd_, conn.fd(), "serverFD");
processEvents(pollFd_); // iteration 2
conn.shutdown(SHUT_WR);
processEvents(pollFd_); // iteration 3
client.shutdown(SHUT_WR);
processEvents(pollFd_); // iteration 4
}
除外,它们可以达到您的期望(此处未显示) )。
Info| TCP connection established to [127.0.0.1:13500]
iteration #1: events on [listenerFD]: [1 = EPOLLIN ]
iteration #1: events on [clientFD]: [4 = EPOLLOUT ]
Info| TCP connection accepted from [127.0.0.1:35160]
iteration #2: events on [serverFD]: [4 = EPOLLOUT ]
// calling serverFD.shutdown(SHUT_WR) here
iteration #3: events on [clientFD]: [2005 = EPOLLIN EPOLLOUT EPOLLRDHUP ] // EPOLLRDHUP arrives, nice.
iteration #3: events on [serverFD]: [4 = EPOLLOUT ] // serverFD (on which I called SHUT_WR) just reported as writable, not cool... but not the main point of the question
// calling clientFD.shutdown(SHUT_WR) here
iteration #4: events on [serverFD]: [2015 = EPOLLIN EPOLLOUT EPOLLRDHUP EPOLLHUP ] // EPOLLRDHUP arrives, nice. EPOLLHUP too!
iteration #4: events on [clientFD]: [2015 = EPOLLIN EPOLLOUT EPOLLRDHUP EPOLLHUP ] // EPOLLHUP on the other side as well. Why? What does EPOLLHUP mean actually?
输出:
<?php
$input = file_get_contents("input.json");
$json = json_decode($input, true);
$required_income = $json['required_income'];
$sms = array();
$index = 0;
$suma = 0;
function imoka($suma, $json, &$index, &$sms) {
for($i = 3; $i >= 0; $i--){
if($suma + $json['sms_list'][$i]['income'] <= $json['required_income']) {
$sms[$index] = $json['sms_list'][$i]['income'];
$index = $index + 1;
return imoka($suma + $json['sms_list'][$i]['income'], $json, $index, $sms);
}
}
}
imoka($suma, $json, $index, $sms);
for($i = 0; $i < $index; $i++){
echo $sms[$i] . '<br>';
}
?>
除了 EPOLLHUP是什么意思以外,没有更好的方法来重述该问题。我认为documentation很差,而其他地方(例如here和here)的信息是错误的或无用的。
注意:要考虑回答的Q,我想确认EPOLLHUP在两个方向的最终FIN-ACK上都升高。
答案 0 :(得分:6)
对于此类问题,use the source!除其他有趣的评论外,还有以下文字:
EPOLLHUP
是 UNMASKABLE 事件(...)。这意味着在我们收到EOF
之后,poll
总是立即返回,使得poll()
上处于状态write()
的{{1}}不可能CLOSE_WAIT
。很明显的一种解决方案---仅当在两个方向上都EPOLLHUP
时才设置shutdown
。
然后是唯一设置EPOLLHUP
的代码:
if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
mask |= EPOLLHUP;
SHUTDOWN_MASK
等于RCV_SHUTDOWN |SEND_SHUTDOWN
。
TL; DR;没错,仅当读写同时关闭时才发送此标志(我认为对等的关闭写入等同于我关闭读取)。当然,或者当连接关闭时。
更新:通过更详细地阅读源代码,这些是我的结论。
关于shutdown
:
shutdown(SHUT_WR)
发送一个FIN
并用SEND_SHUTDOWN
标记套接字。shutdown(SHUT_RD)
不会发送任何内容,并用RCV_SHUTDOWN
标记套接字。FIN
时,将套接字标记为RCV_SHUTDOWN
。关于epoll
:
SEND_SHUTDOWN
和RCV_SHUTDOWN
,则poll
将返回EPOLLHUP
。RCV_SHUTDOWN
,则poll
将返回EPOLLRDHUP
。因此HUP
事件可以理解为:
EPOLLRDHUP
:您已收到FIN
或已致电shutdown(SHUT_RD)
。在任何情况下,您的读取半插槽都处于挂起状态,也就是说,您将不再读取任何数据。EPOLLHUP
:两个插座都挂了。阅读半插口就像上一点,对于发送半插口,您做了类似shutdown(SHUT_WR)
的事情。要完成正常关机,我会这样做:
shutdown(SHUT_WR)
发送FIN
并标记发送数据结束。EPOLLRDHUP
。PS :关于您的评论:
由于写作部分是封闭的,因此变得可写是违反直觉的
如果您理解epoll
的输出不是 ready 而是不会阻塞,那么实际上是可以预期的。也就是说,如果您获得EPOLLOUT
,则可以保证不会阻塞调用write()
。当然,在shutdown(SHUT_WR)
之后,write()
将立即返回。