How do I use EPOLLHUP
You use EPOLLRDHUP
to detect peer shutdown, not EPOLLHUP
(which signals an unexpected close of the socket, i.e. usually an internal error).
Using it is really simple, just "or" the flag with any other flags that you are giving to epoll_ctl
. So, for example instead of EPOLLIN
write EPOLLIN|EPOLLRDHUP
.
After epoll_wait
, do an if(my_event.events & EPOLLRDHUP)
followed by whatever you want to do if the other side closed the connection (you'll probably want to close the socket).
Note that getting a "zero bytes read" result when reading from a socket also means that the other end has shut down the connection, so you should always check for that too, to avoid nasty surprises (the FIN
might arrive after you have woken up from EPOLLIN
but before you call read
, if you are in ET mode, you'll not get another notification).
TCP: When is EPOLLHUP generated?
For these kind of questions, use the source! Among other interesting comments, there is this text:
EPOLLHUP
is UNMASKABLE event (...). It means that after we receivedEOF
,poll
always returns immediately, making impossiblepoll()
onwrite()
in stateCLOSE_WAIT
. One solution is evident --- to setEPOLLHUP
if and only ifshutdown
has been made in both directions.
And then the only code that sets EPOLLHUP
:
if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
mask |= EPOLLHUP;
Being SHUTDOWN_MASK
equal to RCV_SHUTDOWN |SEND_SHUTDOWN
.
TL; DR; You are right, this flag is only sent when the shutdown has been both for read and write (I reckon that the peer shutdowning the write equals to my shutdowning the read). Or when the connection is closed, of course.
UPDATE: From reading the source code with more detail, these are my conclusions.
About shutdown
:
- Doing
shutdown(SHUT_WR)
sends aFIN
and marks the socket withSEND_SHUTDOWN
. - Doing
shutdown(SHUT_RD)
sends nothing and marks the socket withRCV_SHUTDOWN
. - Receiving a
FIN
marks the socket withRCV_SHUTDOWN
.
And about epoll
:
- If the socket is marked with
SEND_SHUTDOWN
andRCV_SHUTDOWN
,poll
will returnEPOLLHUP
. - If the socket is marked with
RCV_SHUTDOWN
,poll
will returnEPOLLRDHUP
.
So the HUP
events can be read as:
EPOLLRDHUP
: you have receivedFIN
or you have calledshutdown(SHUT_RD)
. In any case your reading half-socket is hung, that is, you will read no more data.EPOLLHUP
: you have both half-sockets hung. The reading half-socket is just like the previous point, For the sending half-socket you did something likeshutdown(SHUT_WR)
.
To complete a a graceful shutdown I would do:
- Do
shutdown(SHUT_WR)
to send aFIN
and mark the end of sending data. - Wait for the peer to do the same by polling until you get a
EPOLLRDHUP
. - Now you can close the socket with grace.
PS: About your comment:
it's counterintuitive to get writable, as the writing half is closed
It is actually expected if you understand the output of epoll
not as ready but as will not block. That is, if you get EPOLLOUT
you have the guarantee that calling write()
will not block. And certainly, after shutdown(SHUT_WR)
, write()
will return immediately.
Why am I getting the EPOLLHUP event on a brand new socket
As my example in the comments shows, it seems you can't poll the socket before it's properly initialized, unless you want to handle EPOLLHUP
.
As for the question, no, you won't miss any events. Calling listen()
then epoll()
is the same you'd have to do otherwise (listen()
+ blocking accept()
); actual incoming connections between those calls are handled by the kernel and stay waiting until your code handles them.
Can EPOLLHUP trigger an event for a descriptor, disabled by EPOLLONESHOT?
Seems like disabled descriptors don't receive EPOLLHUP
.
Related Topics
Linux Kernel Device Driver to Dma from a Device into User-Space Memory
Linux Command History with Date and Time
How to Fix the Rust Error "Linker 'Cc' Not Found" for Debian on Windows 10
Linux Tool to Send Raw Data to a Tcp Server
Unshare --Pid /Bin/Bash - Fork Cannot Allocate Memory
.Net-Core: Equivalent of Ildasm/Ilasm
Vagrant Synced Folders Not Working Real-Time on Virtualbox
Disable Yum Transaction Check for File Conflict
Linux - Bash - Get $Releasever and $Basearch Values
Minimizing Copies When Writing Large Data to a Socket
Bash: Run an Executable File in Background
What Is the Explanation of This X86 Hello World Using 32-Bit Int 0X80 Linux System Calls from _Start
Linux Why Can't I Pipe Find Result to Rm
How to Set a Global Nofile Limit to Avoid "Many Open Files" Error
Handling Input Confirmations in Linux Shell Scripting