C语言Socket通信问题:服务端进程无法接收客户端后续消息
问题排查:TCP长连接下服务端卡在
accept、客户端发送无响应 我正在开发一个客户端-服务端项目,客户端采用多线程架构,计算完成后将结果发送至另一进程的服务端。需求是客户端仅建立1次Socket连接并发送多条消息,但目前首次发送正常后便停止;客户端无报错持续发送,服务端却卡在accept函数处无法接收后续消息。相关代码如下:
服务端代码
int listenToSocket(int p) { int socket_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_len; SocketInterface data; // Create socket socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd == -1) { perror("Error creating socket"); return -1; } // Prepare server address structure server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); // Bind the socket to the specified port if (bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Error binding socket"); close(socket_fd); return -1; } // Start listening on the socket if (listen(socket_fd, 1) < 0) { perror("Error listening on socket"); close(socket_fd); return -1; } // Accept client connections and receive data continuously for(;; ) { client_len = sizeof(client_addr); client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { perror("Error accepting client connection"); continue; } // Receive data into the struct if (read(client_fd, &data, sizeof(data)) < 0) { perror("Error receiving data"); close(client_fd); continue; } if(data.over){ break; } // Process the received struct data printf("%d %3.2f %3.2f %50s\n", data.fd.n, data.fd.avg, data.fd.std, data.fd.path); //close(client_fd); memset(&client_addr, 0, sizeof (client_addr)); } // Close the socket close(socket_fd); return 0; }
客户端Socket创建代码(main.c)
// create socket connection int sockfd; struct sockaddr_in server_addr; // Create socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("Error opening socket"); } // Configure server address server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = INADDR_ANY; // Connect to server if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { close(sockfd); perror("Error connecting to server"); kill(pid, SIGTERM); return -1; }
客户端线程发送函数
int sendThroughSocket(FileData *fd, int sockfd) { SocketInterface isock; if(fd != NULL){ strcpy(isock.fd.path, fd->path); isock.fd.avg = fd->avg; isock.fd.std = fd->std; isock.fd.n = fd->n; isock.over = 0; } if (fd == NULL) { isock.over = 1; } if(fd != NULL){ printf("Filename: %s, avg: %f std: %f\n",fd->path, fd->avg, fd->std); }else{ printf("NUll is being sent\n"); } if (write(sockfd, &isock, sizeof(isock)) < 0) { printf("Over: %d\n", isock.over); perror("Error sending data"); return -1; } if(isock.over == 1){ close(sockfd); } return 0; }
核心问题分析
- 服务端逻辑错误:重复调用
accept
服务端的for(;;)循环中每次都会执行accept,意味着处理完第一次连接的一条消息后,会再次阻塞等待新连接。但客户端用同一个长连接发送多条消息,不会发起新连接,导致服务端卡在accept。 - 服务端消息接收逻辑不连续
当前服务端在accept后仅调用一次read,处理完一条消息就回到循环开头执行accept,没有持续读取同一个连接的后续消息。 - 多线程发送的线程安全隐患
多个线程共用同一个sockfd发送数据,可能导致TCP数据流粘包或发送操作互相干扰。
修复方案
服务端修改:固定连接,持续读取消息
将accept移出循环,接受一次连接后,持续从该连接读取消息直到结束:
int listenToSocket(int p) { int socket_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_len; SocketInterface data; // Create socket socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd == -1) { perror("Error creating socket"); return -1; } // Prepare server address structure server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); // Bind the socket to the specified port if (bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Error binding socket"); close(socket_fd); return -1; } // Start listening on the socket if (listen(socket_fd, 1) < 0) { perror("Error listening on socket"); close(socket_fd); return -1; } // 仅接受一次客户端长连接 client_len = sizeof(client_addr); client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { perror("Error accepting client connection"); close(socket_fd); return -1; } // 持续从同一连接读取消息 for(;; ) { memset(&data, 0, sizeof(data)); // 清空结构体,避免残留数据 ssize_t read_len = read(client_fd, &data, sizeof(data)); if (read_len < 0) { perror("Error receiving data"); break; } else if (read_len == 0) { printf("Client closed connection\n"); break; } if(data.over){ printf("Received end signal\n"); break; } printf("%d %3.2f %3.2f %50s\n", data.fd.n, data.fd.avg, data.fd.std, data.fd.path); } close(client_fd); close(socket_fd); return 0; }
客户端优化:线程安全发送+完善错误处理
- 给发送操作加互斥锁,避免并发干扰:
#include <pthread.h> pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER; int sendThroughSocket(FileData *fd, int sockfd) { SocketInterface isock; memset(&isock, 0, sizeof(isock)); // 初始化结构体,清除垃圾数据 if(fd != NULL){ strcpy(isock.fd.path, fd->path); isock.fd.avg = fd->avg; isock.fd.std = fd->std; isock.fd.n = fd->n; isock.over = 0; } else { isock.over = 1; } if(fd != NULL){ printf("Filename: %s, avg: %f std: %f\n",fd->path, fd->avg, fd->std); }else{ printf("Null is being sent\n"); } // 加锁保证发送操作原子性 pthread_mutex_lock(&sock_mutex); ssize_t write_len = write(sockfd, &isock, sizeof(isock)); pthread_mutex_unlock(&sock_mutex); if (write_len < 0) { printf("Over: %d\n", isock.over); perror("Error sending data"); return -1; } else if (write_len != sizeof(isock)) { printf("Warning: only sent %zd bytes out of %zd\n", write_len, sizeof(isock)); // 可补充剩余数据的发送逻辑 } if(isock.over == 1){ close(sockfd); pthread_mutex_destroy(&sock_mutex); } return 0; }
- 完善Socket创建的错误处理:
// Create socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("Error opening socket"); kill(pid, SIGTERM); return -1; }
额外注意事项
- TCP粘包问题:当前固定结构体长度的读取方式可工作,若后续结构体有变动,建议在消息前添加长度字段,避免粘包导致解析错误。
- 连接异常处理:服务端和客户端需处理连接中断情况,比如
read返回0(客户端关闭)或-1(错误)时,及时关闭套接字并退出循环。
内容的提问来源于stack exchange,提问作者mike
相关产品推荐
相关产品推荐

