9

有一个线程是这样的:

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

因为线程在 recv() 调用时阻塞,我怎样才能让线程优雅地退出?

4

7 回答 7

5

您可以致电:-

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

看看这个

于 2013-08-16T06:36:33.930 回答
5

不要在recv上阻塞。而是在“选择”上实现一个块并测试输入 fdset 以确定是否有任何内容要首先读取。

如果有数据要读取,调用recv。如果不是,则循环检查“正在运行”的易失性布尔变量,该变量在启动关闭时由另一个线程设置。

于 2013-08-16T06:39:15.237 回答
4

任何一个:

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

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

于 2013-08-18T06:55:30.433 回答
2

我可能会使用@alk 的好答案(也在此处讨论)中的信号。

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

在初始化时,创建一个全局管道 (2)。当需要结束程序时,关闭管道的写端——现在读端将立即 select(2)/poll(2) 可读(对于 EOF)。同时,让你的recv-blocked线程包括这个管道的读端以及它们的套接字,例如,一个无限期阻塞的select(2)调用。如果管道的读取端返回可读,则 I/O 线程知道是时候优雅地终止了。

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

于 2013-08-16T13:14:27.543 回答
2

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

于 2013-08-16T09:57:34.663 回答
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对并发访问的保护。

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

于 2013-08-16T06:37:17.137 回答
0

另外,将您的套接字设置为非阻塞:

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");
    }
}
于 2013-08-16T06:43:29.440 回答