TCP: When is EPOLLHUP generated?

前端 未结 1 1788
长发绾君心
长发绾君心 2021-02-02 02:41

Also see this question, unanswered as of now.

There is a lot of confusion about EPOLLHUP, even in the man and Kernel docs. People seem to belie

1条回答
  •  说谎
    说谎 (楼主)
    2021-02-02 02:55

    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 received EOF, poll always returns immediately, making impossible poll() on write() in state CLOSE_WAIT. One solution is evident --- to set EPOLLHUP if and only if shutdown 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:

    1. Doing shutdown(SHUT_WR) sends a FIN and marks the socket with SEND_SHUTDOWN.
    2. Doing shutdown(SHUT_RD) sends nothing and marks the socket with RCV_SHUTDOWN.
    3. Receiving a FIN marks the socket with RCV_SHUTDOWN.

    And about epoll:

    1. If the socket is marked with SEND_SHUTDOWN and RCV_SHUTDOWN, poll will return EPOLLHUP.
    2. If the socket is marked with RCV_SHUTDOWN, poll will return EPOLLRDHUP.

    So the HUP events can be read as:

    1. EPOLLRDHUP: you have received FIN or you have called shutdown(SHUT_RD). In any case your reading half-socket is hung, that is, you will read no more data.
    2. 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 like shutdown(SHUT_WR).

    To complete a a graceful shutdown I would do:

    1. Do shutdown(SHUT_WR) to send a FIN and mark the end of sending data.
    2. Wait for the peer to do the same by polling until you get a EPOLLRDHUP.
    3. 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.

    0 讨论(0)
提交回复
热议问题