带有C套接字和pthread的多线程TCP服务器 - 为什么accept()会阻塞第二个请求?

时间:2017-02-12 16:03:57

标签: c sockets tcp server pthreads

我一直在使用unix套接字和pthreads在C中编写一个小型多线程TCP服务器,但我遇到了accept()问题。它会挂起第二个请求,只有在先前的线程退出时才会解锁。

以下是我设置服务器套接字的方法。

int server_start(server_t *server, int port) {
    int fd;
    struct sockaddr_in server_addr;

    // Socket file descriptor.
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket failed");
        return 1;
    }

    // Socket address.
    server_addr.sin_family      = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port        = htons(port);

    // Bind.
    if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
        perror("bind failed");
        return 1;
    }

    server->fd = fd;
    listen(server->fd, server->clients_len);
    pthread_create(&(server->thread), NULL, thread_entry_server, server);

    return 0;
}

这是我的add_client代码。它为客户端生成一个单独的线程。

client_t *server_add_client(server_t *server) {

    int iter,
        fd,
        status;
    client_t *client;

    printf("before\n");
    fd = accept(server->fd, NULL, 0);
    printf("after\n");

    if (fd == -1) {
        perror("accept");
        return NULL; // Connection failed.
    }

    // Find an empty spot.
    client = server->get_empty_spot();
    client->fd = fd;

    // Start the new thread.
    status = pthread_create(
        &(client->thread),
        NULL,
        thread_entry_client,
        client
    );
    if (status != 0) {
        perror("pthread_create");
        close(client->fd);
        return NULL;
    }

    client->active = 1;

    return client;
}

这是我的客户端线程的入口函数:

void *thread_entry_client(void *void_client) {

    client_t *client = void_client;
    int len;

    while (1) {

        len = recv(client->fd, client->recv_buffer, RECV_BUFFER_LEN, 0);
        if (len < 0) {
            perror("recv");
            client->active = 0;
            close(client->fd);
            return NULL;
        }
        if (len == 0) { // Client disconnected.
            client->active = 0;
            close(client->fd);
            printf("disconnect\n");
            return NULL;
        }

        if (len > 0) {
            //printf("%s\n", client->recv_buffer);
            printf("msg\n");
        }
    }

    return NULL;
}

所以我正在做的测试是建立两个连接。第一个连接通过并正常工作,但第二个连接没有 - 而是线程挂起在accept()上。我从printfs(我已经离开那里)知道了这一点,并且我知道在第一个客户端断开连接后,accept()解除阻塞。我也知道我的代码没有关闭服务器套接字文件描述符或更改它。

有关调试的建议吗?我无法理解。

编辑:这是thread_entry_server。

void *thread_entry_server(void *void_server) {
    server_t *server = void_server;
    client_t *client;

    while (1) {
        client = server_add_client(server);
        if (client == NULL) // Server is full or connection failed.
            continue;
    }

    return NULL;
}

2 个答案:

答案 0 :(得分:0)

这是因为accept()将阻止(除非另有配置),直到客户端连接可用。

请参阅提及的Documentation -

  

如果侦听队列没有连接请求且O_NONBLOCK是   没有设置套接字的文件描述符,accept()将阻塞   直到存在连接。如果listen()队列为空   连接请求和O_NONBLOCK在文件描述符上设置   套接字,accept()将失败并将errno设置为[EAGAIN]或   [EWOULDBLOCK]。

此外,server->get_empty_spot();应该始终返回新的client个实例,否则就意味着您将同一个client->thread传递给pthread_create

我通常更喜欢自己创建新帖子,比如 -

**pthread_t newListner;**
ThreadArgs thread_args;
thread_args.client_socket = client_socket;

int rc;

if ((rc = pthread_create(&newListner, NULL, startListener, &thread_args)))
{ .. }

试一试。

答案 1 :(得分:0)

我正在使用javascript websocket进行测试,但我没有进行任何握手,因此它没有完成连接。使用telnet进行测试。