TCP套接字编程中客户端接收模块工作异常问题咨询
TCP套接字通信接收侧故障排查
项目整体链路
当前开发的TCP通信程序业务流程为:
- 客户端第一步从服务端拉取目标路径下的所有文件列表
- 客户端向服务端发送需要下载的指定文件名
- 客户端接收服务端返回的对应文件内容
目前已经通过服务端日志校验,所有发送流程均按代码逻辑执行完成、所有数据包均已推送至内核发送缓冲区,问题完全集中在客户端接收逻辑与发送/接收协议的适配层。
故障1:文件列表接收循环无法退出
现象
服务端文件列表的发送循环已执行完毕,所有文件名均序列化后通过write接口推送完成,但客户端的接收循环始终无法终止。
对应代码
客户端接收逻辑:
while ((read_cnt=read(sd, buf, BUF_SIZE)) != 0) { read_cnt=read(sd, buf, BUF_SIZE); printf("%s\n", buf); }
服务端列表发送逻辑:
while ((entry=readdir(dir)) != NULL) { sprintf(buf, "%s\n", entry->d_name); write(clnt_sd, buf, strlen(buf)); }
原因定位
- 循环内重复调用两次
read:循环入口先执行一次read拿到数据做循环判断,进入循环块后又立刻执行第二次read。第一次读到的内容直接被丢弃,第二次读到的内容才会被打印,数据读取逻辑完全错位。 - 未正确适配TCP流的结束判定规则:TCP是面向字节流的协议,本身没有消息边界,只有对端主动关闭连接、发送FIN包完成流终止流程时,阻塞状态下的
read才会返回0。服务端发完所有列表数据后没有主动关闭连接,也没有发送流结束标识,read会一直阻塞等待新数据,永远不会触发循环退出条件。 - 未处理读异常分支:当前循环判断仅校验
read_cnt != 0,没有处理read返回-1的系统调用异常场景(比如连接被重置、网络中断)。一旦返回-1,循环条件依然成立,会陷入异常循环。
故障2:消息接收不全、文件传输量不匹配、结束标识失效
现象
- 服务端累计推送17条消息,客户端仅收到15条
- 预设的收到"fin"标识后退出接收循环的逻辑未触发
- 进入文件接收阶段后,服务端已完成全部文件内容发送,客户端统计的接收总字节数和服务端实际发送总量始终不匹配,循环无法正常终止
对应代码
客户端文件接收逻辑:
while (1) { read_cnt=read(sd, buf, BUF_SIZE); fwrite((void*)buf, 1, read_cnt, fp); printf("%s\n", buf); if (strstr(buf, "fin") != NULL) { break; } total_read += read_cnt; pcnt = ((total_read/ file_size) * 100.0)/10; printf("(%.0d %%) Send %.0f / %.0f bytes.\n", pcnt*10, total_read, file_size); }
服务端文件发送逻辑:
FILE *fp; fp = fopen(file_name, "r"); if (fp == NULL) { printf("File not Exist"); exit(1); } fseek(fp, 0, SEEK_END); int file_size = ftell(fp); sprintf(buf, "%d", file_size); write(clnt_sd, buf, read_cnt); fseek(fp, 0, SEEK_SET); while (feof(fp) == 0) { read_cnt = fread((void*)buf, 1, BUF_SIZE, fp); write(clnt_sd, buf, read_cnt); } fclose(fp);
原因定位
- 文件大小包头发送长度错误:服务端把文件大小转成字符串写入缓冲区后,调用
write时的长度参数错误使用了变量read_cnt(此时该变量存的是上一次操作的残留值,并非当前包头的实际长度),没有用strlen(buf)计算真实的包头长度,导致客户端收到的文件大小基准值完全随机错位,后续所有基于文件大小的进度计算、传输终止判断从源头就是错的。 - 结束标识检测未适配TCP粘包拆包特性:客户端每收到一次
read返回的缓冲区数据就调用strstr匹配"fin",但TCP是流协议,没有消息边界,"fin"字符串可能被拆分到两次连续的read返回结果中(比如第一次读到"fi",第二次读到"n"),单次缓冲区扫描永远匹配不到完整的标识;如果文件本身的内容里包含"fin"字符串,还会出现提前退出的误判。 - 服务端文件循环发送逻辑存在偏差:用
feof(fp)做循环判断的时机错位,会比预期多读一次满缓冲区的数据才退出循环,导致客户端实际收到的字节数比文件真实大小多。 - 传输结束后没有明确的终止信号:服务端发完所有文件内容后,没有和客户端同步约定结束标识,也没有关闭连接,
read会一直阻塞在等待数据的状态,根本走不到终止分支。 - 之前尝试用
memset、bzero清空接收缓冲区没有效果,因为故障根源根本不是缓冲区残留脏数据,是代码逻辑错位、没有适配TCP流的传输特性。
内容的提问来源于stack exchange,提问作者user15157814
相关产品推荐
相关产品推荐

