发送端必须先读取文件内容到内存缓冲区再发送,不能直接用send()传文件路径;需用std::ifstream或open()读取后调用send()发送二进制数据,否则易因传指针而非内容导致ebadf或eagain错误。

发送端必须先读文件再发,不能直接 send(fd, filename, ...)
很多人以为 send() 能直接传文件路径,其实它只收内存地址和长度。你得自己用 std::ifstream 或 open() 读进缓冲区,再调用 send() 发送二进制数据。
常见错误现象:send() 返回 -1 且 errno 是 EBADF(句柄无效)或 EAGAIN(非阻塞模式下无数据可发),本质是传了字符串指针而非文件内容。
- 推荐用
std::ifstream配合std::vector<char></char>一次性加载小文件( - 大文件务必分块读取+分块发送,避免内存爆炸,每块建议 64KB–1MB
- 发送前检查
socket是否已连接成功(connect()返回 0),否则send()会失败
必须处理 send() 的返回值,它不保证一次发完
send() 可能只发出部分字节,尤其在网络拥塞或缓冲区满时。忽略返回值会导致文件截断——接收端等不到剩余数据,卡死或校验失败。
正确做法是循环调用 send(),直到所有数据发完:
ssize_t sent = 0; while (sent
- 不要用
MSG_WAITALL:它对 TCP socket 无效,仅对某些特殊协议有意义 - 阻塞 socket 下仍可能返回
n ,不能假设一次成功 - 若用
sendfile()(Linux),需确保sockfd是支持写入的 socket,且offset正确推进
接收端要按长度循环 recv(),不能只靠一次 recv()
发送端分多次 send(),接收端就必须对应多次 recv(),否则会漏数据。TCP 不保留消息边界,recv() 返回的字节数完全不可预测。
典型错误:开一个大缓冲区,调一次 recv() 就认为文件收完了——实际只收到了前几个 KB。
- 发送端应在文件前发一个固定长度头(如 8 字节
uint64_t表示文件大小),接收端先recv()头,再循环收正文 - 每次
recv()后检查返回值:0 表示对端关闭连接,-1 且errno == EAGAIN表示暂无数据 - 写入磁盘时用
write()或ofstream.write(),别用操作符(会触发格式化,破坏二进制)
跨平台要注意 sendfile() 和 ioctl() 的可用性
Linux 下可用 sendfile() 零拷贝发文件,但 Windows 没这函数,macOS 的 sendfile() 接口不同(参数顺序、标志位都不一样)。硬依赖它会让代码无法移植。
更稳妥的做法是统一用用户态缓冲区 + 循环 send(),性能损失在千兆网下几乎不可测(实测 100MB 文件慢 2% 左右)。
- Windows 替代方案:用
TransmitFile(),但需 HANDLE 而非 fd,且 socket 必须是 overlapped 模式 - macOS 注意:
sendfile()第二个参数是intfd,第三个是off_t*offset,和 Linux 相反 - 如果用
ioctl(sockfd, FIONREAD, &bytes)判断可读字节数,Windows 不支持FIONREAD,得换ioctlsocket()+FIONREAD
实际跑通的关键不在“怎么发”,而在“怎么确认发全了、收全了”。头协议、返回值检查、循环收发,这三件事漏任何一环,文件就大概率损坏。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











