You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程TCP服务器无法处理多客户端问题求助

问题分析与解决方案

让我看看你的问题——这个多线程服务器的冻结问题其实是全局变量误用导致的,咱们一步步拆解清楚:

核心问题诊断

你的服务器出现第一个客户端冻结的根本原因有这几个:

  • 全局变量client_sock会在每次新客户端连接时被accept的结果覆盖,第一个线程本来要处理自己的客户端套接字,结果被第二个客户端的套接字替换了,第一个客户端的请求完全没人处理,自然卡在recv上。
  • 线程创建时传递的参数是message,这完全没有意义,线程根本拿不到当前连接对应的客户端套接字,只能复用被覆盖的全局变量。
  • 全局的client_message会被多个线程同时读写,引发数据竞争,导致消息混乱甚至程序异常。

修复方案

我们针对这些问题逐一修改:

1. 移除危险的全局变量

把client_sock、client_message这些需要线程独立使用的变量从全局移到线程内部,或者在创建线程时将当前客户端的套接字作为参数传递给线程。

2. 正确传递线程参数

创建线程时,把accept得到的client_sock作为参数传入(注意用malloc分配内存避免类型转换截断问题),在线程函数里再转回来使用。

3. 完善错误处理

当recv返回0(客户端主动断开)或-1(出错)时,要关闭套接字并退出线程,避免资源泄漏。

4. 避免数据竞争

每个线程使用自己的局部缓冲区,不要用全局的client_message。

修改后的服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

// 线程函数声明,接收客户端套接字作为参数
void *thread_function(void *arg);

int main() {
    int socket_desc, client_sock, c;
    struct sockaddr_in server, client;
    pthread_t a_thread;

    // 创建套接字
    socket_desc = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        printf("Could not create socket");
        return 1;
    }
    puts("Socket created");

    // 配置服务器地址
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    // 绑定套接字
    if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("bind failed. Error");
        return 1;
    }
    puts("bind done");

    // 监听连接
    listen(socket_desc, 3);
    puts("Waiting for incoming connections...");

    // 循环接收客户端连接
    for (;;) {
        c = sizeof(struct sockaddr_in);
        client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
        if (client_sock < 0) {
            perror("accept failed");
            continue; // 出错后继续等待下一个连接,不要直接退出
        }
        puts("Connection accepted");
        printf("Client connected with IP: %s\n", inet_ntoa(client.sin_addr));

        // 为每个客户端创建线程,传递客户端套接字作为参数
        int *client_sock_ptr = malloc(sizeof(int));
        *client_sock_ptr = client_sock;
        if (pthread_create(&a_thread, NULL, thread_function, (void *)client_sock_ptr) != 0) {
            perror("Failed to create thread");
            close(client_sock);
            free(client_sock_ptr);
        }
        pthread_detach(a_thread); // 分离线程,自动释放资源
    }

    return 0;
}

void *thread_function(void *arg) {
    int client_sock = *(int *)arg;
    free(arg); // 释放之前malloc的内存
    char client_message[2000]; // 线程局部缓冲区,避免多线程竞争
    int read_size;

    // 循环接收客户端消息
    while ((read_size = recv(client_sock, client_message, sizeof(client_message) - 1, 0)) > 0) {
        client_message[read_size] = '\0'; // 确保字符串以'\0'结尾
        puts("Received from client:");
        puts(client_message);

        // 回传消息给客户端
        if (send(client_sock, client_message, read_size, 0) < 0) {
            perror("Send failed");
            break;
        }
        memset(client_message, 0, sizeof(client_message)); // 清空缓冲区
    }

    if (read_size == 0) {
        puts("Client disconnected");
    } else if (read_size == -1) {
        perror("recv failed");
    }

    // 关闭客户端套接字
    close(client_sock);
    pthread_exit(NULL);
}

优化后的客户端代码(可选)

主要增加了recv的错误处理,避免客户端无意义地卡住:

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int sock;
    struct sockaddr_in server;
    char message[2000], server_reply[2000];
    int n = 1;
    char sn[20];

    // 创建套接字
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        printf("Could not create socket");
        return 1;
    }
    puts("Socket created");

    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_family = AF_INET;
    server.sin_port = htons(8888);

    // 连接服务器
    if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("connect failed. Error");
        return 1;
    }
    puts("Connected\n");

    // 与服务器通信
    while (1) {
        sprintf(sn, "%d", n);
        strcpy(message, sn);
        printf("Sending: %s\n", message);

        // 发送消息
        if (send(sock, message, strlen(message), 0) < 0) {
            perror("Send failed");
            break;
        }

        // 接收服务器回复
        memset(server_reply, 0, sizeof(server_reply));
        int recv_size = recv(sock, server_reply, sizeof(server_reply) - 1, 0);
        if (recv_size < 0) {
            perror("recv failed");
            break;
        } else if (recv_size == 0) {
            puts("Server disconnected");
            break;
        }
        server_reply[recv_size] = '\0';
        puts("Server reply :");
        puts(server_reply);

        n++;
        sleep(1); // 可选,避免发送太快导致输出混乱
    }

    close(sock);
    return 0;
}

验证效果

现在启动服务器,然后打开多个客户端,每个客户端都会独立发送数字并收到回复,不会再出现第一个客户端冻结的情况了。

内容的提问来源于stack exchange,提问作者kotseman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:29:00