停止在recv()上阻塞的接收器线程

Stopping a receiver thread that blocks on recv()

本文关键字:接收器 线程 recv      更新时间:2023-10-16

我有一个聊天应用程序,它有一个单独的线程来监听传入的消息。

while (main thread not calling for receiver to quit) {
  string message = tcpCon.tcpReceive();    // Relies on the recv() function
  processIncomingMessage(message);
}
这种工作方式有一个大问题。大多数时候,循环将阻塞在recv()上,因此接收线程不会退出。在几秒钟后不强制线程终止的情况下,解决这个问题的正确方法是什么?

使用shutdown()关闭套接字以关闭所有接收器。

这在我的系统上打印出'recv returned 0',表明接收器看到了有序关闭。注释掉shutdown(),看着它永远挂着。

从长远来看,OP应该修复设计,要么使用select,要么在协议中包含显式退出消息。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
/* Free on my system. YMMV */
int port = 7777;
int cd;
void *f(void *arg)
{
    /* Hack: proper code would synchronize here */
    sleep(1);
    /* This works: */
    shutdown(cd, SHUT_RDWR);
    close(cd);
    return 0;
}
int main(void)
{
    /* Create a fake server which sends nothing */
    int sd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in sa = { 0 };
    const int on = 1;
    char buf;
    pthread_t thread;
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons(port);
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
    /* Other error reporting omitted for clarity */
    if (bind(sd, (const struct sockaddr*)&sa, sizeof sa) < 0) {
        perror("bind");
        return EXIT_FAILURE;
    }
    /* Create a client */
    listen(sd, 1);
    cd = socket(AF_INET, SOCK_STREAM, 0);
    connect(cd, (const struct sockaddr*)&sa, sizeof sa);
    accept(sd, 0, 0);
    /* Try to close socket on another thread */
    pthread_create(&thread, 0, f, 0);
    printf("recv returned %dn", recv(cd, &buf, 1, 0));
    pthread_join(thread, 0);
    return 0;
}

您可以使用select()来等待传入的数据,并避免在recv()中阻塞。Select()也会阻塞,但您可以在设定的间隔后超时,以便while循环可以继续并检查来自主线程的退出信号:

while (main thread not calling for receiver to quit) {
  if (tcpCon.hasData(500)) {              // Relies on select() to determine that data is
                                          // available; times out after 500 milliseconds
    string message = tcpCon.tcpReceive(); // Relies on the recv() function
    processIncomingMessage(message);
  }
}

如果在另一个线程中关闭套接字,则recv()将退出。

从任何其他线程调用套接字上的close将使recv调用立即失败。