结论:async_read_some后不能直接用async_write发原buffer,因async_write不立即拷贝数据,_data必须是session成员变量且生命周期覆盖整个异步写过程;session应在handle_read中检测error::eof或bytes_transfered为0时delete this,避免野指针和崩溃。

直接上结论:用 io_context 驱动 tcp::socket,靠 async_read_some + async_write 组合实现应答式 Echo,但必须手动管理 _data 缓冲区生命周期和 Session 对象存活 —— 否则一断连就崩溃或内存泄漏。
为什么 async_read_some 之后不能直接用 async_write 发送原 buffer?
因为 async_write 不保证立即拷贝数据;它只把 buffer 地址和长度记下来,内部可能延迟发送、分片、复用底层 sendmsg 调用。一旦 _data 是栈变量或被 memset 覆盖,或者 Session 被提前 delete this,回调里访问的就是野指针。
-
async_read_some的 buffer 必须在整个异步写完成前保持有效 —— 所以_data得是Session的成员变量,不能是局部数组 -
async_write的 buffer 不能在回调触发前被修改或析构,否则handle_write里读到的是脏数据或崩溃 - 常见错误现象:
Segmentation fault出现在handle_write第一行,或服务器收不到回包但没报错
Session 对象什么时候该 delete?别在 handle_read 里删
客户端断开时,TCP 层会先发 FIN,触发一次“可读”事件 —— 此时 async_read_some 完成,error 是 boost::asio::error::eof,bytes_transfered 为 0。这才是安全删除 Session 的信号点。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 不要在
handle_read的if (!error)分支里删 —— 那只是正常收包路径,删了就丢了后续 write 回调 - 不要在
handle_write里删 —— 写失败可能是临时错误(如 EAGAIN),重试逻辑还没走 - 正确做法:在
handle_read中判断error == boost::asio::error::eof || bytes_transfered == 0,再delete this - 更稳妥的方案:用
std::shared_ptr<session></session>管理生命周期,配合weak_ptr防循环引用
io_context.run() 卡住不返回?检查有没有漏掉 run() 或 stop()
io_context 的事件循环必须有人调用 run(),否则所有 async_* 操作都只是注册进队列,永不触发回调。但一旦 run() 返回,说明队列空了或被 stop() 中断 —— 这会导致新连接无法 accept,老连接回调失联。
- 单线程模型:主线程调
io.run(),不能在中间 return;需要退出时调io.stop(),然后等待 run 返回 - 多线程模型:多个线程同时调
io.run()是安全的,但要注意strand保护共享状态(比如全局连接计数器) - 常见错误现象:程序启动后无响应、客户端能 connect 但 send 数据没回音、log 里完全没打印
server receive data is - 调试技巧:在
main开头加std::cout ,确认是否执行到这一行
最易被忽略的一点:async_write 不等于“发完”,它只表示“已提交发送请求”。如果你依赖“发完才处理下一个请求”,得自己加状态机或用 boost::asio::write(同步版)—— 但那就不是异步 Echo 了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










