Boost::asio WebSocket问题:tcp::no_delay无效,客户端接收消息拼接
问题解决思路
核心原因
你复用了同一个beast::flat_buffer对象m_WriteBuffer,在第一次async_write还未完成时,就再次通过ostream往里面写入新数据,导致两次消息内容被合并到同一个buffer里,最终WebSocket发送的是合并后的帧。
Sleep(100)能临时解决,是因为它让第一次异步write有足够时间完成,buffer在第二次写入前已经处于可用状态,但这是不可靠的临时方案。而tcp::no_delay(true)是禁用TCP Nagle算法,解决的是TCP层的小包合并问题,但你的问题是应用层buffer复用导致的内容合并,和TCP层无关,所以设置它没用。
修复方案
方案一:每次发送使用独立的buffer
不要复用同一个flat_buffer,每次发送消息时创建新的buffer对象,确保每次write的是独立的消息内容。修改后的发送代码示例:
// 每次发送创建新的flat_buffer,用智能指针管理生命周期 auto write_buffer = std::make_shared<beast::flat_buffer>(); boost::beast::ostream(*write_buffer) << message; ws_.async_write(write_buffer->data(), beast::bind_front_handler( &WebSocketSession::on_write, shared_from_this(), write_buffer)); // 传递智能指针,确保buffer存活到write完成
需要同步调整on_write函数,接收这个buffer的智能指针参数,避免buffer提前被销毁。
方案二:维护发送队列确保顺序发送
添加一个消息队列,当有新消息要发送时,如果当前没有正在执行的write操作就立即发送;否则将消息加入队列,等上一次on_write完成后再发送队列里的下一条消息。示例思路:
// 类成员变量 std::queue<std::string> m_send_queue; bool m_is_writing = false; // 对外暴露的发送接口 void send(std::string message) { bool was_empty = m_send_queue.empty(); m_send_queue.push(std::move(message)); if (was_empty && !m_is_writing) { do_write(); } } // 实际执行发送的函数 void do_write() { m_is_writing = true; auto& curr_msg = m_send_queue.front(); auto write_buffer = std::make_shared<beast::flat_buffer>(); boost::beast::ostream(*write_buffer) << curr_msg; ws_.async_write(write_buffer->data(), beast::bind_front_handler( &WebSocketSession::on_write, shared_from_this(), write_buffer)); } // write完成后的回调处理 void on_write(boost::system::error_code ec, std::size_t bytes_transferred, std::shared_ptr<beast::flat_buffer>) { // 处理错误逻辑 if (ec) { // 错误处理代码 return; } // 清理已发送的消息,继续发送队列中的下一条 m_send_queue.pop(); m_is_writing = false; if (!m_send_queue.empty()) { do_write(); } }
额外提示
- WebSocket本身是基于帧的协议,只要你正确发送每个独立的帧,客户端就能收到独立的消息,无需依赖TCP层的设置。
- 异步操作中必须注意资源生命周期,用智能指针管理buffer等资源,避免因对象提前销毁导致的未定义行为。
内容的提问来源于stack exchange,提问作者Lalit Sharma
相关产品推荐
相关产品推荐

