You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Beast WebSocket客户端连接异常排查求助

问题原因分析

1. Strand资源未重置

WebSocket流对象ws_在构造时绑定了strand,但重连时复用了原对象,旧strand可能因之前的错误处于停止状态,导致后续操作被直接取消。

2. 同步阻塞导致IO上下文停滞

reconnect()方法中使用std::this_thread::sleep_for同步阻塞线程,而io_context可能运行在同一个线程上,阻塞期间事件循环无法处理任何事件,触发系统级的操作取消错误。

3. WebSocket流状态残留

重连时未重置WebSocket流对象的内部状态,之前连接失败的错误状态残留,导致新的连接操作直接触发旧错误。

4. 缺少心跳维持连接

服务器可能因长时间无数据交互主动断开连接,客户端没有发送心跳帧维持连接,断开后短时间内重连可能被服务器限制,导致立即失败。

修复方案

1. 重置WebSocket流与Strand

在close()方法后重新初始化WebSocket流对象,确保每次重连使用全新的strand和流状态:

void WebSocketClientOrder::reconnect() {
    close();
    // 手动析构旧流对象并重新初始化
    ws_.~stream();
    new (&ws_) websocket::stream<asio::ssl::stream<tcp::socket>>(
        asio::make_strand(io_context_), 
        ws_.next_layer().get_ssl_context()
    );
    // 异步延迟重连,避免阻塞IO线程
    asio::steady_timer timer(io_context_);
    timer.expires_after(std::chrono::seconds(RECONNECTION_TIMEOUT));
    timer.async_wait([this](beast::error_code ec) {
        if (!ec) {
            connect(host_, port_);
        }
    });
}

2. 替换同步sleep为异步延迟

移除std::this_thread::sleep_for,改用Boost.Asio的steady_timer实现异步延迟,确保IO上下文始终能处理事件。

3. 重连时清空消息队列

在重连前清空消息队列,避免旧消息在新连接中错误发送:

void WebSocketClientOrder::reconnect() {
    close();
    // 清空消息队列
    {
        std::lock_guard<std::mutex> lock(message_queue_mutex_);
        while (!message_queue_.empty()) {
            message_queue_.pop();
        }
    }
    // ... 异步延迟重连代码
}

4. 添加心跳机制

定期发送ping帧维持连接,避免服务器因无交互断开连接:

// 类内新增成员变量
asio::steady_timer heartbeat_timer_;
const std::chrono::seconds HEARTBEAT_INTERVAL = std::chrono::seconds(30);

// 构造函数初始化定时器
WebSocketClientOrder::WebSocketClientOrder(asio::io_context& io_context, asio::ssl::context& ssl_context,
                                           std::string api_key, std::string api_secret)
    : io_context_(io_context)
    , resolver_(io_context)
    , ws_(asio::make_strand(io_context), ssl_context)
    , heartbeat_timer_(io_context)
    , api_key_(std::move(api_key))
    , api_secret_(std::move(api_secret)) {}

// 心跳发送函数
void WebSocketClientOrder::send_heartbeat() {
    if (!ws_.is_open()) {
        return;
    }
    ws_.async_ping("", [this](beast::error_code ec) {
        if (!ec) {
            heartbeat_timer_.expires_after(HEARTBEAT_INTERVAL);
            heartbeat_timer_.async_wait([this](beast::error_code ec) {
                if (!ec) {
                    send_heartbeat();
                }
            });
        }
    });
}

// 连接成功后启动心跳
void WebSocketClientOrder::connect(const std::string& host, const std::string& port) {
    host_ = host;
    port_ = port;

    try {
        auto results = resolver_.resolve(host, port);
        asio::connect(ws_.next_layer().next_layer(), results);

        if (!SSL_set_tlsext_host_name(ws_.next_layer().native_handle(), host.c_str())) {
            throw beast::system_error(
                beast::error_code(static_cast<int>(::ERR_get_error()), asio::error::get_ssl_category()),
                "[WSCO]: Failed to set SNI"
            );
        }

        ws_.next_layer().handshake(asio::ssl::stream_base::client);
        ws_.handshake(host + ":" + port, DEFAULT_URI);
        std::cout << "[WSCO]: Connected to " << host << std::endl;

        authenticate();
        do_read();
        send_heartbeat(); // 启动心跳
    } catch (const std::exception& e) {
        std::cerr << "[WSCO]: Connection failed: " << e.what() << ". Retrying..." << std::endl;
        reconnect();
    }
}

5. 优化close方法的资源清理

确保彻底清理所有层级的连接状态,取消定时器避免残留事件:

void WebSocketClientOrder::close() {
    beast::error_code ec;
    // 关闭WebSocket
    if (ws_.is_open()) {
        ws_.close(websocket::close_code::normal, ec);
        if (ec && ec != beast::errc::not_connected) {
            std::cerr << "[WSCO]: Close failed: " << ec.message() << std::endl;
        }
    }
    // 关闭SSL流
    ws_.next_layer().shutdown(ec);
    if (ec && ec != asio::error::eof && ec != asio::ssl::error::stream_truncated && ec != beast::errc::not_connected) {
        std::cerr << "[WSCO]: SSL shutdown failed: " << ec.message() << std::endl;
    }
    // 关闭底层socket
    ws_.next_layer().lowest_layer().close(ec);
    if (ec && ec != beast::errc::not_connected) {
        std::cerr << "[WSCO]: Socket close failed: " << ec.message() << std::endl;
    }
    // 取消心跳定时器
    heartbeat_timer_.cancel(ec);
}

内容的提问来源于stack exchange,提问作者stas stas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:40:54