macOS下C语言Socket服务端客户端程序异常排查
问题描述
在macOS环境下用C语言开发了Socket服务端与客户端程序,需求是处理普通输入及Date、Time、带时区的Time命令,程序持续运行直到输入“Exit Server”终止。但测试时发现,输入“Date”或“Time”命令后程序无法正常工作,且程序停止正常运行时的循环次数每次都不一样。
示例输出:
Input: Date Output: date Input: Time Output: time Input: nsigos Output: SOGISN Input: fsn Output: NSF Input: fsjfs Output: SFJSF Input: Date Output: date date //此处不再显示"Input: ",程序似乎无法回到while循环开头 //普通输入时,服务端会返回客户端发送字符串的反转大写形式
服务端代码
#include <netinet/in.h> //储存地址信息的结构 #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> //Socket API #include <sys/types.h> #include <string.h> #include <arpa/inet.h> #include <unistd.h> #include <time.h> char *strremove(char *str, const char *sub); int main(int argc, char const* argv[]) { // 创建服务端Socket,和客户端程序逻辑类似 int servSockD = socket(AF_INET, SOCK_STREAM, 0); // 存储要发送给客户端的数据 char serMsg[255]; char tocontinue[9]="continue"; char toend[4]="end"; // 定义服务端地址 struct sockaddr_in servAddr; servAddr.sin_family = AF_INET; servAddr.sin_port = htons(9001); servAddr.sin_addr.s_addr = INADDR_ANY; // 将Socket绑定到指定IP和端口 bind(servSockD, (struct sockaddr*)&servAddr, sizeof(servAddr)); // 监听连接请求 listen(servSockD, 1); // 保存客户端Socket描述符 int clientSocket = accept(servSockD, NULL, NULL); struct sockaddr_in clientAddr; socklen_t clientAddrSize = sizeof(clientAddr); getpeername(clientSocket, (struct sockaddr*)&clientAddr, &clientAddrSize); char* clientIP = inet_ntoa(clientAddr.sin_addr); printf("Client IP address: %s\n", clientIP); struct sockaddr_in clientLocalAddr; socklen_t clientLocalAddrSize = sizeof(clientLocalAddr); getsockname(clientSocket, (struct sockaddr*)&clientLocalAddr, &clientLocalAddrSize); int clientPort = ntohs(clientLocalAddr.sin_port); printf("Client Port number: %d\n", clientPort); while(1){ send(clientSocket, tocontinue, sizeof(tocontinue), 0); recv(clientSocket, serMsg, sizeof(serMsg), 0); size_t serlength = strlen(serMsg); if (serlength > 0 && (serMsg[serlength - 1] == '\n' || serMsg[serlength - 1] == '\r')) { serMsg[serlength - 1] = '\0'; // 替换换行符为字符串结束符 } if(strcmp(serMsg, "Exit Server")==0 || strcmp(serMsg, "Exit Client")==0){ break; } else if(strcmp(serMsg, "Date")==0){ char buffer[50]="date"; //这里只发短消息测试循环是否正常 send(clientSocket, buffer, sizeof(buffer), 0); memset(buffer, 0, sizeof(buffer)); } else if(strcmp(serMsg, "Time")==0){ char buffer[50]="time"; send(clientSocket, buffer, sizeof(buffer), 0); memset(buffer, 0, sizeof(buffer)); } else if(strncmp(serMsg, "Time", 4)==0){ char buffer[50]="time zone"; send(clientSocket, buffer, sizeof(buffer), 0); memset(buffer, 0, sizeof(buffer)); } else{ int length = strlen(serMsg); int start = 0; int end = length - 1; printf("Length of message: %d\n", length); while (start < end) { // 交换首尾字符 char temp = serMsg[start]; serMsg[start] = serMsg[end]; serMsg[end] = temp; // 向字符串中心移动指针 start++; end--; } for(int i=0; i<length; i++){ if(serMsg[i]>=97 && serMsg[i]<=122){ serMsg[i]=serMsg[i]-32; } } // 发送消息到客户端Socket send(clientSocket, serMsg, sizeof(serMsg), 0); memset(serMsg, 0, sizeof(serMsg)); } } send(clientSocket, toend, sizeof(toend), 0); close(clientSocket); return 0; } char *strremove(char *str, const char *sub) { size_t len = strlen(sub); if (len > 0) { char *p = str; while ((p = strstr(p, sub)) != NULL) { memmove(p, p + len, strlen(p + len) + 1); } } return str; }
客户端代码
#include <netinet/in.h> //储存地址信息的结构 #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> //Socket API #include <sys/types.h> #include <string.h> #include <unistd.h> int main(int argc, char const* argv[]) { int sockD = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in servAddr; servAddr.sin_family = AF_INET; servAddr.sin_port = htons(9001); // 使用未被占用的端口 servAddr.sin_addr.s_addr = INADDR_ANY; int connectStatus = connect(sockD, (struct sockaddr*)&servAddr, sizeof(servAddr)); if (connectStatus == -1) { printf("Error...\n"); } else { while(1){ char strData[255]; char checkifend[10]=""; recv(sockD, checkifend, sizeof(checkifend), 0); if(strcmp(checkifend, "end")==0){ break; } printf("Input: "); fgets(strData, sizeof(strData), stdin); send(sockD, strData, strlen(strData)+1, 0); recv(sockD, strData, sizeof(strData), 0); if(strcmp(strData, "end")==0){ break; } printf("Output: %s\n", strData); } } close(sockD); return 0; }
问题原因及修复方案
核心问题1:Socket数据发送/接收长度不匹配,残留数据干扰逻辑
服务端处理Date/Time等命令时,发送的是缓冲区总长度(如50字节),但有效字符串长度远小于该值,剩余空间的垃圾数据会留在Socket缓冲区。客户端下次接收时会读取这些残留数据,导致程序逻辑混乱。
修复:发送时仅传递有效字符串长度(包含终止符)
将服务端所有send语句的长度参数从sizeof(buffer)/sizeof(serMsg)改为strlen(buffer)+1或strlen(serMsg)+1,比如:
// 原代码 send(clientSocket, buffer, sizeof(buffer), 0); // 修改后 send(clientSocket, buffer, strlen(buffer)+1, 0);
核心问题2:接收缓冲区未初始化,strlen计算异常
服务端的serMsg、客户端的checkifend和strData在接收数据前未清空,若上一次接收的数据长度大于本次,strlen会读取到之前的终止符后的垃圾数据,导致字符串比较、处理错误。
修复:每次接收前清空缓冲区
- 服务端循环内
recv前添加:memset(serMsg, 0, sizeof(serMsg)); - 客户端循环内接收
checkifend前添加:memset(checkifend, 0, sizeof(checkifend)); - 客户端接收响应前添加:
memset(strData, 0, sizeof(strData));
核心问题3:Time命令判断顺序错误
原代码先判断精确匹配"Time",再判断前缀匹配"Time",导致带时区的Time命令(如Time UTC)会被精确匹配拦截,无法触发时区逻辑。
修复:调整判断顺序,先处理带时区的情况
将else if(strncmp(serMsg, "Time", 4)==0)放在else if(strcmp(serMsg, "Time")==0)之前。
修复后的关键代码片段
服务端循环修改
while(1){ send(clientSocket, tocontinue, strlen(tocontinue)+1, 0); memset(serMsg, 0, sizeof(serMsg)); recv(clientSocket, serMsg, sizeof(serMsg), 0); // 处理换行符代码不变 if(strcmp(serMsg, "Exit Server")==0 || strcmp(serMsg, "Exit Client")==0){ break; } else if(strncmp(serMsg, "Time", 4)==0){ char buffer[50]="time zone"; send(clientSocket, buffer, strlen(buffer)+1, 0); memset(buffer, 0, sizeof(buffer)); } else if(strcmp(serMsg, "Date")==0){ char buffer[50]="date"; send(clientSocket, buffer, strlen(buffer)+1, 0); memset(buffer, 0, sizeof(buffer)); } else if(strcmp(serMsg, "Time")==0){ char buffer[50]="time"; send(clientSocket, buffer, strlen(buffer)+1, 0); memset(buffer, 0, sizeof(buffer)); } else{ // 反转大写逻辑不变 send(clientSocket, serMsg, strlen(serMsg)+1, 0); memset(serMsg, 0, sizeof(serMsg)); } }
客户端循环修改
while(1){ char strData[255]; char checkifend[10]=""; memset(checkifend, 0, sizeof(checkifend)); recv(sockD, checkifend, sizeof(checkifend), 0); if(strcmp(checkifend, "end")==0){ break; } printf("Input: "); fgets(strData, sizeof(strData), stdin); send(sockD, strData, strlen(strData)+1, 0); memset(strData, 0, sizeof(strData)); recv(sockD, strData, sizeof(strData), 0); if(strcmp(strData, "end")==0){ break; } printf("Output: %s\n", strData); }
内容的提问来源于stack exchange,提问作者bongbong
相关产品推荐
相关产品推荐

