c-如何让在recv()上阻塞的线程优雅地退出



有这样一个线程:

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

因为线程在recv()调用上被阻塞,我如何让线程优雅地退出?

您可以调用:-

shutdown(sock, SHUT_RDWR)    //on the remote end

看看这个。

不要阻止recv。相反,在"select"上实现一个块,并测试输入fdset,以确定是否首先要读取任何内容。

如果有要读取的数据,请调用recv。如果没有,则循环,检查"isrunning"意志文件布尔变量,该变量在启动关闭时由其他线程设置。

任一:

  1. setsockopt()SO_RCVTIMEO设置一个读取超时,每当它触发时,检查一个状态变量,看看你是否告诉自己停止读取
  2. 如果您想永远停止读取套接字,请关闭它以使用shutdown(sd, SHUT_RD)进行输入。这将导致recv()从现在起返回零
  3. 将套接字设置为非阻塞模式,并使用带有超时的select()来告诉您何时读取,采用与上面(1)中的状态变量相同的策略。然而,非阻塞模式会给发送操作带来相当大的复杂性,因此您应该更喜欢上面的(1)或(2)

您的伪代码缺少EOS和错误检查。我希望它看起来不是那样的。

声明一些"stop"布尔值,在每次recv()返回后检查它,如果设置了它,则终止。要关闭,请设置bool并从另一个线程关闭套接字。阻塞recv()将返回"立即"并返回一个错误,但这无关紧要,因为您无论如何都要终止:)

我可能会像@alk的精细答案一样使用信号(也在这里讨论)。

或者,您可以使用多路复用I/O。

初始化时,创建一个全局管道(2)。当该结束程序时,关闭管道的写入端——现在读取端将立即选择(2)/poll(2)可读(用于EOF)。同时,让recv阻塞的线程将该管道的读取端及其套接字包括在例如无限期阻塞的select(2)调用中。如果管道的读取端返回可读,则I/O线程知道是时候正常终止了。

这种技术的主要警告是确保写端只关闭一次,因为随后的天真关闭(2)可能会夹住一些无辜的文件,而这些文件恰好被赋予了与管道旧写端相同的描述符。

声明全局退出标志:

int bExit = 0;

让电路部件测试一下:

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

要破坏读卡器,首先设置退出标志

bExit = 1;

然后向读取器线程发送信号

pthread_kill(pthreadReader, SIGUSR1);

注:在这个例子中,我遗漏了bExit对并发访问的保护。

这可以通过使用互斥或适当的声明来实现。

此外,将套接字设置为非阻塞:

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);
}

然后,您可以在套接字上调用recv(),它不会阻塞。如果没有可读取的内容,则ERRNO全局变量设置为常数EWOULDBLOCK

int rc = recv(listen_sd, buffer, sizeof(buffer), 0);
if (rc < 0) {
    if (errno != EWOULDBLOCK) {
        perror("recv() failed");
    }
}

最新更新