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

C语言TCP Socket通信客户端偶现无法读取服务端发送数据问题

问题根因

1. 核心原因:TCP是字节流协议,不存在内置消息边界

你客户端读取文件名的代码调用了read(sock, buffer, sizeof(buffer)),会一次性读取内核缓冲区中最多1024字节的数据:

  • 如果服务端发送的文件名和后续文件内容被合并到同一个TCP段,或者在第一次read触发前已经全部到达客户端内核缓冲区,这次read会把文件名+所有文件内容全部读到buffer里
  • 你只提取了buffer开头的文件名使用,剩下的文件内容没有做任何处理,直接被后续的memset覆盖丢弃
  • 后面的while循环调用read时已经没有剩余数据可读取,直接返回0结束,所以你看不到任何内容打印

这就是问题复现概率随机的原因:只有当TCP分包刚好把文件名单独作为一个段发送时,第一次read只会读取到文件名,后续内容才会被while循环读到。

2. 其他次要问题

  • 服务端发送完所有文件内容后,没有主动关闭连接套接字,客户端不会收到EOF通知,极端场景下会一直阻塞在read调用上
  • 缺少必要的错误校验:比如服务端fopen打开源文件没有判断返回值,如果源文件不存在会直接无数据发送

修复方案

快速修复(适配现有逻辑)

  1. 服务端发完数据后主动关闭连接,在fclose(f)后添加如下代码:
// 发送FIN包告知客户端数据已全部发送完成
shutdown(new_socket, SHUT_WR);
close(new_socket);
close(sock);
  1. 客户端读取文件名后,先处理buffer中剩余的文件内容,修改读取文件名的代码段:
// Get file name from server
ssize_t read_len = read(sock, buffer, sizeof(buffer));
if(read_len == -1){
    perror("Could not read filename from server");
    exit(EXIT_FAILURE);
}
// 计算文件名长度,提取后续剩余内容
int fname_total_len = strlen(buffer) + 1;
ssize_t remain_content_len = read_len - fname_total_len;
if (remain_content_len > 0) {
    // 把第一次读到的剩余内容先写入文件
    printf("Read returned %zd\n", remain_content_len);
    printf("Received %s", buffer + fname_total_len);
    fputs(buffer + fname_total_len, f);
}
memset(buffer, 0, sizeof(buffer));

规范实现方案(彻底解决边界问题)

提前约定通信格式,避免多读少读问题:

  1. 服务端先发送4字节整数,表示文件名的字节长度
  2. 再发送对应长度的文件名内容
  3. 再发送4字节整数,表示文件的总字节大小
  4. 最后发送对应长度的文件内容
  5. 客户端严格按照约定的长度读取对应数据,不会出现跨包读取的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:57:03