8

From the manual of epoll_ctl:

EPOLLRDHUP (since Linux 2.6.17)

Stream socket peer closed connection, or shut down writing half of connection. (This flag is especially useful for writing simple code to detect peer shutdown when using Edge Triggered monitoring.)

From the manual of recv:

If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0.

It seems to me then that both of the above cover the same scenarios, and that as long as I catch EPOLLRDHUP events first, I should never receive a read() or recv() of length 0 (and thus don't need to bother checking for such). But is this guaranteed to be true?

Will
  • 2,014
  • 2
  • 19
  • 42

1 Answers1

8

If you get an event with EPOLLRDHUP=1 then just close the connection right away without reading. If you get an event with EPOLLRDHUP=0 and EPOLLIN=1 then go ahead and read, but you should be prepared to handle the possibility of recv() still returning 0, just in case. Perhaps a FIN arrives after you got EPOLLIN=1 but before you actually call recv().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 4
    "Perhaps a FIN arrives after you got EPOLLIN=1 but before you actually call recv()." But even if a FIN arrives at such a moment, recv() would return something bigger than 0, right? Because the FIFO pipe still contains whatever elicited EPOLLIN=1 in the first place. – Will May 10 '13 at 00:54
  • Great answer. I would amend it to say, ".. go ahead and read, but you should be prepared to handle the possibility of recv() still returning 0 **or -1**, just in case." – selbie May 10 '13 at 01:20
  • @WillBuddha: if you check the result of epoll for `EPOLLERR=1` and/or `EPOLLHUP=1` to detect error events, do you still check `recv()` for -1? Yes, because it is good error handling. Same thing with `EPOLLRDHUP=1` and `recv()=0`. Is it *likely* to happen? No. Is it *possible* to happen? Yes. Maybe the kernel screws up internally. Maybe it gets things out of order. Who knows. Better to cover your @$$, just in case. – Remy Lebeau May 10 '13 at 01:58
  • Alright.... point taken. Thanks, I'll accept your answer with the rationale of "check for 0, because kernel developers might screw up". – Will May 10 '13 at 02:11
  • 3
    I don't agree with this answer. I think the part with "If you get an event with EPOLLRDHUP=1" is wrong, because it happened to me to get both EPOLLIN and EPOLLRDHUP at once, if the peer sent something and immediately closed the connection. So, there was data to read. – haelix Nov 06 '14 at 15:58
  • I would be very surprised if `epoll` reports both `EPOLLIN=1` and `EPOLLRDHUP=1` in the same event. Either there is data to read, or the socket is closed. But, if you really want, you could check for `EPOLLIN=1` first and do a read, and then close the socket if the read fails or `EPOLLRDHUP=1`. – Remy Lebeau Nov 06 '14 at 17:50
  • @RemyLebeau it does. I have an application which used to check for RDHUP and IN in that order. It had the bug that if the peer sends something (such as a termination reason) and immediately closes, my app was not receiving that data because it checked for RDHUP first and killed the connection. From what I've seen, this happens when both endpoints are on the same machine, not sure it happens on different machines. – haelix Nov 07 '14 at 08:14
  • 2
    @haelix I am looking at this right now, and can confirm this. A fast write/close on localhost will result in 0x2001 (RDHUP|IN), but with data in the pipe, and read returning first the data, then zero. Also for example a SIGPIPE will result in 0x2019 (RDHUP|HUP|ERR|IN) with a read returning zero. In this edge case you could skip the read and just act on the RDHUP, but in the case above you prob want the data sent, which in my mind makes RDHUP pretty useless since you anyway need to handle this when reading. – Fredrik Widlund May 31 '15 at 12:40