不会崩溃,但会因socket未连接导致boost::asio::error::not_connected或非法内存访问;正确做法是仅在connect_handler且error为空时调用async_read,并用remote_endpoint验证连接。

async_connect后立刻调用async_read会崩溃?
常见错误是连接还没建立成功,就直接在未初始化的socket_上调用async_read。Boost.Asio不会帮你判断socket是否已连接——它只保证回调触发时socket处于可用状态(如果连接成功),但你若在connect_handler外提前访问,大概率遇到boost::asio::error::not_connected或访问非法内存。
正确做法是:所有后续IO操作(async_read、async_write)必须严格放在connect_handler内部,且仅当error为空时才发起。
- 不要在
Session构造函数里直接调用async_read - 确保
socket_.is_open()为true且socket_.is_connected()(需自己封装,Asio不提供该成员函数) - 推荐用
socket_.remote_endpoint(error)检查是否可获取远端地址,作为连接成功的轻量验证
为什么shared_ptr<Session>不能只捕获this?
客户端对象生命周期比连接更短——比如用户主动关闭界面,Session对象析构,但异步操作还在排队等待执行。若回调里捕获的是裸指针this,就会访问已释放内存,引发段错误或静默数据损坏。
必须用std::enable_shared_from_this<session></session>配合shared_from_this()传入回调:
立即学习“C++免费学习笔记(深入)”;
socket_.async_connect(
endpoint_,
[self = shared_from_this()](const boost::system::error_code& error) {
if (!error) {
self->do_read(); // self持有有效引用
}
});
- 切忌在lambda中写
[this]或[ptr = this] - 若Session未继承
enable_shared_from_this,shared_from_this()会抛std::bad_weak_ptr - 每次发起新异步操作前,都要重新调用
shared_from_this()生成新引用,避免因上一个回调结束导致引用计数归零
如何避免write并发导致数据交错?
客户端常有“边收边发”需求,比如收到服务端指令后立即回ACK。若多个线程或多次回调同时调用async_write,buffer可能被覆盖、顺序错乱,甚至触发boost::asio::error::operation_not_supported(底层socket被重复启动写操作)。
根本解法不是加锁,而是串行化写操作:
- 维护一个
std::deque<:vector>></:vector>作为待发队列 - 只允许一个
async_write处于进行中;写完成回调里弹出队首,再对下一个发起async_write - 对外提供
send(const std::vector<char>& data)</char>接口,只负责入队和触发首写(若当前无活跃写) - 不用
strand——客户端通常单线程驱动io_context::run(),strand反而增加调度开销
timeout控制该用deadline_timer还是steady_timer?
用boost::asio::steady_timer。理由很实际:deadline_timer基于系统时钟,一旦用户手动调整系统时间(如NTP校准、运维误操作),定时器可能提前触发或永远不触发;而steady_timer基于单调时钟,不受系统时间跳变影响。
典型用法是为每个关键操作(connect、read、write)单独配一个timer,在操作发起时expires_after(30s),并在完成回调里调用cancel()清除。
- 不要复用同一个timer对象处理多个操作——cancel后状态不清,易漏触发
- timer回调里的
error == boost::asio::error::operation_aborted表示操作已正常完成,无需处理 - 若timer回调先于IO回调执行,需在IO回调里检查
timer_.expires_at() == boost::asio::steady_timer::time_point::max()来判断是否已超时
最易被忽略的点是:客户端没有服务端那种“连接池压力”,但更容易因网络抖动或服务端异常陷入半开连接。务必在async_read的error分支里检查error == boost::asio::error::eof或error == boost::asio::error::connection_reset,并主动销毁Session对象——否则shared_ptr引用计数不降,资源泄漏会随连接数缓慢爬升。



















