因为tcp::socket只处理无结构的网络字节流,不识别文件边界或大小,必须自定义协议(如长度前缀)并用async_read/async_write配合streambuf、dynamic_buffer、超时控制等机制安全传输。

为什么不能直接用 boost::asio::ip::tcp::socket 读写文件?
因为 boost::asio::ip::tcp::socket 只处理网络字节流,不感知文件边界、大小或结构。直接 read_some() 文件内容再发出去,接收端根本不知道“哪段是文件头”“文件到哪结束”。常见错误是发送端用 write() 发完就 close,接收端却卡在 read_some() 等不到 EOF(TCP 没有内置文件结束信号),或者收到乱码——本质是没定义协议。
必须自己约定传输格式,最简单可靠的是“长度前缀 + 数据”:先发 4 字节整数表示后续文件内容长度,再发原始字节。接收端先读 4 字节,解析出长度 n,再循环读满 n 字节。
如何用 async_read 和 async_write 安全传输任意大小文件?
大文件不能一次性加载进内存,得边读磁盘边发;也不能一次 async_write 整个 buffer,得拆成小块并保证顺序。关键点在于:发送端用 std::ifstream 配合 boost::asio::streambuf 流式读取,接收端用 boost::asio::dynamic_buffer 动态累积数据。
- 发送端:先
async_write4 字节长度头,成功后再用async_sendfile(Linux)或分块async_write(跨平台)发文件本体 - 接收端:先
async_read4 字节长度头,成功后调用自定义 handler 读取对应字节数,避免缓冲区溢出 - 务必检查每次异步操作的
error_code,比如boost::asio::error::eof表示文件读完,boost::asio::error::connection_reset要清理资源
async_sendfile 在 Linux 上高效但 Windows 不可用怎么办?
async_sendfile 利用内核 zero-copy,比用户态读-写快得多,但它只在 Linux 支持(底层调用 sendfile(2))。Windows 没等价异步接口,强行用 TransmitFile 又难和 asio 的 completion handler 对齐。
立即学习“C++免费学习笔记(深入)”;
跨平台稳妥做法是退回到分块读写:
- 发送端开一个固定大小 buffer(如 64KB),用
std::ifstream::read()填充,再async_write这段 buffer - 每次
async_write完成后,在 handler 里继续读下一块,直到ifstream.eof() - 注意:不要在 handler 里直接递归调用
async_write,容易栈溢出;改用循环 +post()或状态机管理
接收端如何防止内存暴涨或超时卡死?
如果对方发了个 10GB 文件但没发长度头,或长度字段被篡改成 2^32-1,接收端盲目分配 buffer 会 OOM。必须设硬性上限和超时。
- 在解析长度头后,立刻检查数值是否合理(比如
length > 2 * 1024 * 1024 * 1024就 reject) - 用
boost::asio::steady_timer绑定整个接收流程:从读长度头开始计时,超时则cancel()所有 pending 操作 - 接收文件数据时,用
boost::asio::dynamic_buffer替代std::vector,它支持按需扩容,但也要配max_size()限制
实际项目里,文件传输的难点从来不在“怎么发”,而在“怎么安全地收”——长度校验、内存控制、连接异常恢复,这些细节漏掉一个,服务就可能被恶意输入拖垮。


















