Given any epoll TCP socket event, if EPOLLRDHUP=0 and EPOLLIN=1; is a subsequent call to read()/recv() guaranteed to return a read size unequal to 0?
Asked Answered
C

1

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?

Cog answered 10/5, 2013 at 0:36 Comment(0)
P
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().

Pickar answered 10/5, 2013 at 0:46 Comment(8)
"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.Cog
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."Circumvolution
@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.Pickar
Alright.... point taken. Thanks, I'll accept your answer with the rationale of "check for 0, because kernel developers might screw up".Cog
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.Warbler
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.Pickar
@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.Warbler
@Warbler 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.Obnubilate

© 2022 - 2024 — McMap. All rights reserved.