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请求不符合服务器协议要求,导致服务器返回错误格式。 - 修复:
- 用抓包工具(如Wireshark)或
telnet/nc手动发送请求,确认服务器实际返回格式。 - 拆分解析流程:先读取并解析头部(
RHL status Fname Fsize),再根据Fsize的数值,单独读取对应长度的二进制文件数据。
- 用抓包工具(如Wireshark)或
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
相关产品推荐
相关产品推荐

