C语言使用pthread实现Socket客户端与服务器连接异常排查
问题分析与解决方案
看起来你遇到的问题主要来自三个核心点:线程函数签名不匹配、主线程提前退出,以及潜在的时序竞争问题,我们逐个拆解解决:
1. 线程函数签名不符合pthread要求
pthread_create要求线程入口函数的签名必须是void* (*)(void*),但你的server()和client()都是返回int且无参数的函数,这会触发未定义行为,线程可能无法正常启动或执行逻辑。
你需要把这两个函数改成符合要求的签名,内部业务逻辑可以保留,最后返回NULL或者把返回值转为void*类型。
2. 主线程提前退出导致子线程被强制终止
你的main函数创建完两个线程后直接结束,这会导致整个进程终止,所有子线程会被系统强制杀死。服务器线程可能还没完成listen初始化就被终止,或者刚进入accept阻塞状态就被干掉;客户端线程也可能还没完成连接就提前退出。
必须在main函数里调用pthread_join等待两个子线程执行完毕,才能保证整个流程完整运行。
3. 时序竞争:客户端可能在服务器就绪前发起连接
客户端线程可能比服务器线程先完成初始化并调用connect,此时服务器还没完成bind和listen操作,会导致连接失败,客户端线程退出后,服务器线程就一直卡在accept状态无法退出。可以在客户端线程里加一个短暂延迟,或者用更严谨的同步机制(比如条件变量)让客户端等待服务器准备就绪。
修改后的完整代码
#include <stdio.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #define PORT 8080 #define NUM_THREADS 2 // 调整线程函数签名以符合pthread要求 void* server(void* arg) { int server_fd, new_socket, valread; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = {0}; char *hello = "Hello from server"; // 创建套接字文件描述符 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 强制将套接字绑定到8080端口 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定套接字到指定端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) { perror("bind failed"); exit(EXIT_FAILURE); } // 开始监听连接 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } printf("Server is listening on port %d...\n", PORT); // 阻塞等待客户端连接 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) { perror("accept"); exit(EXIT_FAILURE); } valread = read(new_socket, buffer, 1024); printf("Server received: %s\n", buffer); send(new_socket, hello, strlen(hello), 0); printf("Server sent hello message\n"); // 关闭套接字,避免资源泄漏 close(new_socket); close(server_fd); return NULL; } void* client(void* arg) { // 短暂延迟,等待服务器完成初始化(严谨场景可改用条件变量同步) sleep(1); int sock = 0, valread; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[1024] = {0}; // 创建客户端套接字 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return (void*)-1; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 将IP地址转为二进制格式 if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) { printf("\nInvalid address/ Address not supported \n"); return (void*)-1; } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection Failed \n"); return (void*)-1; } send(sock, hello, strlen(hello), 0); printf("Client sent hello message\n"); valread = read(sock, buffer, 1024); printf("Client received: %s\n", buffer); // 关闭套接字 close(sock); return NULL; } int main(){ pthread_t threads[NUM_THREADS]; int ret; printf("In main: creating thread server\n"); ret = pthread_create(&threads[0], NULL, server, NULL); if (ret){ printf("ERROR; return code from pthread_create() is %d\n", ret); exit(-1); } printf("In main: creating thread client\n"); ret = pthread_create(&threads[1], NULL, client, NULL); if (ret){ printf("ERROR; return code from pthread_create() is %d\n", ret); exit(-1); } // 等待两个子线程执行完毕 pthread_join(threads[0], NULL); pthread_join(threads[1], NULL); printf("Main thread exiting\n"); return 0; }
额外说明
- 我添加了
sleep(1)让客户端等待服务器初始化,这是一个简单的时序问题解决方案。如果需要更严谨的同步,可以使用pthread_cond_t条件变量,让服务器在完成listen后主动通知客户端发起连接。 - 代码中补充了关闭套接字的操作,避免系统资源泄漏。
- 编译时记得链接pthread库:
gcc -o socket_thread socket_thread.c -lpthread
运行后你应该能看到服务器和客户端正常收发消息的输出了。
内容的提问来源于stack exchange,提问作者Linir
相关产品推荐
相关产品推荐

