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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:59:53