async_connect立即完成且状态为error::operation_aborted,是因提前调用socket_.cancel()或io_context::stop()导致操作被中止;需确保resolver生命周期足够长、handler首行检查ec、handshake成功后再read/write、binary/text模式在发送前正确设置。
连接失败时 async_connect 立即完成但状态为 error::operation_aborted
这不是网络错误,而是你提前调用了 socket_.cancel() 或 io_context::stop(),导致挂起的异步操作被强制中止。beast 的 websocket 连接流程依赖底层 tcp 连接成功后才发起 http 升级,所以必须确保 async_connect 的 completion handler 被真正执行,而不是被取消淹没。
常见诱因:tcp::resolver 异步解析和 async_connect 之间没做 lifetime 管理,lambda 捕获了局部对象(比如临时 tcp::resolver 实例),导致 resolver 析构时 cancel 所有 pending 操作;或者在 handler 中未检查 ec 就直接调用 ws_.async_handshake(...)。
- resolver 必须是成员变量或通过
shared_from_this()延长生命周期 - 每个 async 操作的 handler 都要以
if (ec) return;开头 - 不要在 resolver handler 里直接 new 一个临时对象去调
async_connect,容易悬垂
ws_.async_handshake 返回 websocket::error::upgrade_declined
服务端拒绝了 WebSocket 升级请求,通常因为 HTTP 状态码不是 101,比如返回了 400、403 或 500。Beast 默认发送的 Upgrade 请求头是合规的,但某些代理或网关会拦截或改写 Host、Origin、Sec-WebSocket-Key 等字段。
可抓包确认:用 tcpdump 或 Wireshark 看客户端发出的 GET 请求是否含 Upgrade: websocket 和 Connection: Upgrade,响应是否为 HTTP/1.1 101 Switching Protocols。若服务端是自建,检查是否正确设置了 res.set(http::field::sec_websocket_accept, ...)。
- 手动设置
req.set(http::field::origin, "http://localhost")可绕过部分 Origin 校验 - 若服务端要求特定子协议,需在 handshake 前调用
ws_.set_option(websocket::stream_base::decorator(...))注入Sec-WebSocket-Protocol - Beast 从 v290 起默认禁用自动重定向,遇到 301/302 不会自动跳转,需自己处理 Location 头
读取时 ws_.async_read 没触发 handler,也没有报错
最可能的原因是:WebSocket 流尚未进入 open 状态,但你已开始调用 async_read。Beast 要求必须等 async_handshake 成功完成、且 ws_.is_open() == true 后才能读写。另一个常见情况是 buffer 太小 —— async_read 默认使用 net::dynamic_buffer,但如果传入的是固定大小的 net::mutable_buffer 且容量不足,它会静默等待更多数据(而非报错)。
- 务必在 handshake handler 中检查
ws_.is_open(),再启动 read 循环 - 读取推荐用
net::dynamic_buffer<:string></:string>或beast::flat_buffer,避免长度限制 - 不要复用同一个
flat_buffer对象跨多次async_read调用,每次应调用buffer.clear() - 如果服务端发的是 ping,而你没设置
ws_.set_option(websocket::stream_base::auto_ping{true}),可能导致连接被单向静默关闭
发送二进制消息时 ws_.text(false) 不生效
ws_.text(false) 只影响**后续**发送帧的 FIN + opcode,不会修改已构造的 message。如果你在调用 ws_.write 或 ws_.async_write 前没设好,Beast 仍按 text 帧发送(opcode = 1),服务端可能直接断连。
更稳妥的做法是显式控制每条消息:用 websocket::frame_type::binary 构造 websocket::message_buffer,或对 net::const_buffer 调用 ws_.async_write(net::const_buffer{data, size}, ...) 前确保 ws_.binary(true) 已设置。
-
ws_.binary(true)和ws_.text(false)是等价的,选其一即可,但必须在 write 前调用 - 若混合发送 text/binary,每次 send 前都应显式设置,别依赖上一次状态
- Beast v300+ 对空 binary 帧(size=0)有 bug,建议至少填 1 字节 padding 避免 hang
Beast 的异步模型不隐藏状态机细节,所有“没反应”几乎都对应某个状态没到位或 buffer 生命周期失控。比起照抄示例,盯住 is_open()、buffer.size()、ec.message() 这三个值,比任何文档都管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











