Также см. этот вопрос , без ответа на данный момент.
Существует большая путаница в EPOLLHUP
, даже в документах man
и Kernel.Люди, кажется, полагают, что он возвращается при опросе дескриптора , локально закрытого для записи , то есть shutdown(SHUT_WR)
, то есть того же вызова, который вызывает EPOLLRDHUP
в узле .Но это неправда, в моих экспериментах я получаю EPOLLOUT
, и нет EPOLLHUP
после shutdown(SHUT_WR)
(да, нелогично получать доступный для записи , так как половина записи закрыта, но этоне основной вопрос).
man плохой, потому что он говорит, что EPOLLHUP
происходит, когда Зависание произошло на связанном файловом дескрипторе , безсказать, что значит «повесить трубку» - что сделал пэр?какие пакеты были отправлены? Эта другая статья просто еще больше сбивает с толку и кажется мне совершенно неправильной.
Мои эксперименты показывают, что EPOLLHUP
приходит, когда EOF (пакеты FIN) обмениваются в обе стороны, то есть, когда обе стороны выдают shutdown(SHUT_WR)
.Это не имеет ничего общего с SHUT_RD
, который я никогда не называю.Также не имеет ничего общего с close
.С точки зрения пакетов, у меня есть подозрение, что EPOLLHUP
возникает на подтверждении отправленного FIN хостов, то есть инициатор завершения вызывает это событие на шаге 3 при четырехстороннем установлении соединения, а партнер на шаге 4(см. здесь ).Если подтвердится, это здорово, потому что он заполняет пробел, который я искал, а именно, как опрашивать неблокирующие сокеты для окончательного подтверждения без LINGER. Это правильно?
(примечание: я использую ET, но я не думаю, что это актуально для этого)
Пример кода и вывод.
Код, находящийся в фреймворке, я извлек из него суть, за исключением TcpSocket::createListener
, 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?
Нет лучшего способа перефразировать вопрос, кроме что означает EPOLLHUP ?Я доказал, что документация плохая, а информация в других местах (например, здесь и здесь ) неверна или бесполезна.
Примечание. Чтобы рассмотреть вопрос с ответом Q, я хочу получить подтверждение того, что EPOLLHUP поднят на последних FIN-ACK в обоих направлениях.