How to let a thread which blocks on recv() exit gracefully?

后端 未结 7 1809
暗喜
暗喜 2020-12-11 17:27

There is a thread likes this:

{  
    ......    
    while (1)
    {
        recv(socket, buffer, sizeof(buffer), 0);
        ......
    }
    close(socket         


        
相关标签:
7条回答
  • 2020-12-11 17:45

    Do not block on recv. Rather implement a block on 'select' and test the input fdset to determine if there is anything to read in the first place.

    If there is data to be read, call recv. If not, loop, check 'isrunning' volitile Boolean variable that gets set by the other thread when shutdown has been initiated.

    0 讨论(0)
  • 2020-12-11 17:47

    Declare some 'stop' boolean, check it after every recv() return and terminate if it's set. To shut down, set the bool and close the socket from another thread. The blocking recv() will return 'immediately' with an error, but it does not matter 'cos you're about to terminate anyway:)

    0 讨论(0)
  • 2020-12-11 17:52

    Declare a global exit flag:

    int bExit = 0;
    

    Let the cirtical parts test it:

     while(1)
     {
       ssize_t result = recv(socket, buffer, sizeof(buffer), 0);
       if ((-1 == result) && (EINTR == error) && bExit) 
       {
         break;
       }
    
       ...
     }
    

    To break your reader, first set the exit flag

    bExit = 1;
    

    then send a signal to the reader thread

    pthread_kill(pthreadReader, SIGUSR1);
    

    Note: What I left out in this example is the protection of bExit against concurrent access.

    This might be achieved by using a mutex or an appropriate declaration.

    0 讨论(0)
  • 2020-12-11 17:53

    Either:

    1. Set a read timeout, with setsockopt() and SO_RCVTIMEO, and whenever it triggers check a state variable to see if you've told yourself to stop reading.
    2. If you want to stop reading the socket forever, shut it down for input with shutdown(sd, SHUT_RD). This will cause recv() to return zero from now on.
    3. Set the socket into non-blocking mode and use select() with a timeout to tell you when to read, adopting the same strategy with a state variable as at (1) above. However non-blocking mode introduces considerable complications into the send operation, so you should prefer (1) or (2) above.

    Your pseudo-code lacks EOS- and error-checking. I hope it doesn't really look like that.

    0 讨论(0)
  • 2020-12-11 17:57

    I would likely use signals as in @alk's fine answer (also discussed here).

    Alternatively, you may use multiplexed I/O.

    At initialization, create a global pipe(2). When it is time to end the program, close the write-end of the pipe — now the read-end will instantly select(2)/poll(2) readable (for EOF). Meanwhile, have your recv-blocked threads include the read-end of this pipe along with their sockets in, for example, an indefinitely blocking select(2) call. If the read-end of the pipe returns readable, the I/O threads know it is time to terminate gracefully.

    The major caveat of this technique is to ensure that the write-end is closed only once, as a subsequent, naïve close(2) might nip some innocent file that happens to have been given the same descriptor as the pipe's old write-end.

    0 讨论(0)
  • 2020-12-11 18:04

    Also, set your socket to be nonblocking:

    int listen_sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
    if (listen_sd < 0) {
        perror("socket() failed");
    }
    
    int on = 1;
    
    //set socket to be non-blocking
    int rc = ioctl(listen_sd, FIONBIO,(char *)&on);
    if (rc < 0) {
        perror("ioctl() failed");
        close(listen_sd);
    }
    

    Then, you can call recv() on the socket, and it won't block. If there's nothing to read then the ERRNO global variable is set to the constant EWOULDBLOCK

    int rc = recv(listen_sd, buffer, sizeof(buffer), 0);
    if (rc < 0) {
        if (errno != EWOULDBLOCK) {
            perror("recv() failed");
        }
    }
    
    0 讨论(0)
提交回复
热议问题