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

TCP通信C函数异常:返回数据不符或段错误排查求助

TCP通信C函数问题排查

问题描述

编写了一个与TCP服务器通信的C语言函数,预期向服务器发送信息后,接收格式为RHL status [Fname Fsize Fdata](其中Fname为.PNG/.JPG文件名,Fsize为文件字节数,Fdata为文件内容),但实际收到RHL OK 5 12这类不符合预期的数据,有时还触发段错误。函数代码如下:

char write_buffer[128];
char read_buffer[128];
char hint_status[4];
char Fsize[10];
FILE *score;
char lnk[1024];
int Fsize_max = 1024*1024*1024;

int hint(){
    if (connect(tcpfd, tcpres->ai_addr, tcpres->ai_addrlen) < 0) {
        printf("\n Error : Connect Failed \n");
        return 0;
    }
    sprintf(write_buffer,"GHL %s\n",PLID);
    n=write(tcpfd, write_buffer,strlen(write_buffer));
    n=read(tcpfd, read_buffer, 128);
    sscanf(read_buffer, "%s %s %s %s %s\n", message_type, hint_status, Fname, Fsize, lnk);
    if(strcmp(hint_status, "NOK") == 0){
        return 0;
    };

    if (atoi(Fsize) > Fsize_max){
        printf("size_overflow\n");
        return 0;
    }

    score = fopen(Fname, "w+");
    fwrite(lnk, 1, sizeof(lnk), score);
    fclose(score);
    return 0;
}   

问题排查与修复

1. 未声明/未初始化变量引发未定义行为

代码中message_type、Fname、n、tcpfd、tcpres、PLID均未显式声明,C语言会将未声明变量默认视为int类型,直接使用会导致内存访问错误(如段错误)或数据解析异常。

  • 修复:显式声明所有变量并初始化:
    int tcpfd; // 需提前完成socket创建与初始化
    struct addrinfo *tcpres; // 需提前通过getaddrinfo解析得到
    char *PLID = "your_plid_here"; // 替换为实际PLID值
    char message_type[4]; // 匹配"RHL"长度
    char Fname[256]; // 足够存储常规文件名
    ssize_t n; // read/write返回值为ssize_t类型,避免类型不匹配
    

2. TCP读取未处理返回值,缓冲区无终止符

  • read返回值未检查:若read失败返回-1,read_buffer内容未定义,直接传入sscanf会引发异常;若服务器返回数据超过128字节,read_buffer被填满后无\0终止符,sscanf会越界读取内存,触发段错误。
  • 修复:检查read返回值并手动添加终止符:
    n = read(tcpfd, read_buffer, sizeof(read_buffer)-1); // 预留1字节存终止符
    if (n < 0) {
        perror("read failed");
        return -1;
    }
    read_buffer[n] = '\0'; // 确保字符串终止
    

3. 数据格式解析逻辑完全错误

  • 预期的Fdata是二进制文件内容,不能用%s解析(%s遇到\0即停止,二进制数据必然包含\0);且read_buffer仅128字节,根本无法存储大文件内容。
  • 实际收到RHL OK 5 12说明服务器返回格式与预期不符,或发送的GHL %s\n请求不符合服务器协议要求,导致服务器返回错误格式。
  • 修复:
    1. 用抓包工具(如Wireshark)或telnet/nc手动发送请求,确认服务器实际返回格式。
    2. 拆分解析流程:先读取并解析头部(RHL status Fname Fsize),再根据Fsize的数值,单独读取对应长度的二进制文件数据。

4. 缓冲区长度不足导致溢出

  • hint_status[4]:若服务器返回的状态字符串长度超过3(如"ERROR"),会溢出缓冲区,破坏其他变量内存。
  • Fsize[10]:1GB对应的数值是1073741824,共10位数字,加上\0需要11字节,Fsize[10]会溢出,导致atoi解析错误。
  • 修复:扩大缓冲区长度:
    char hint_status[16]; // 足够存储常见状态字符串
    char Fsize[16]; // 支持到999GB的数值存储
    

5. 文件操作未检查返回值,写入逻辑错误

  • fopen未检查返回值:若文件名无效或无权限,score会为NULL,此时fwrite操作空指针会触发段错误。
  • fwrite使用sizeof(lnk)写入:实际应写入服务器返回的Fdata长度(即Fsize对应的数值),而非lnk的总长度,且lnk并未正确存储Fdata。
  • 修复:
    score = fopen(Fname, "wb+"); // 用二进制模式写入,避免换行符自动转换
    if (score == NULL) {
        perror("fopen failed");
        return -1;
    }
    // 假设已正确读取Fdata到fdata_buffer,长度为file_size
    fwrite(fdata_buffer, 1, file_size, score);
    

6. 重复连接问题

每次调用hint都执行connect,若之前已建立连接,重复调用会导致错误,应仅在未连接状态下执行连接操作。

内容的提问来源于stack exchange,提问作者ak3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:45:32