多线程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
相关产品推荐
相关产品推荐

