From 222e816d5e8312c25c1173e16773943c495bbaf1 Mon Sep 17 00:00:00 2001 From: cxh Date: Thu, 22 Jan 2026 09:37:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B7=B2=E7=9F=A5bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tunnel_client.cpp | 196 +++++++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 90 deletions(-) diff --git a/src/tunnel_client.cpp b/src/tunnel_client.cpp index e30d697..e288b2a 100644 --- a/src/tunnel_client.cpp +++ b/src/tunnel_client.cpp @@ -45,21 +45,24 @@ void TunnelClient::start() void TunnelClient::stop() { running_.store(false); - if (client_) + + ws_client* cli = client_; // 拷贝一份,避免 race + if (cli) { - client_->get_io_service().post( - [this]() + cli->get_io_service().post( + [this, cli]() { try { websocketpp::lib::error_code ec; - client_->close(hdl_, websocketpp::close::status::going_away, "stop", ec); + cli->close(hdl_, websocketpp::close::status::going_away, "stop", ec); } catch (...) { } }); } + if (th_.joinable()) th_.join(); } @@ -204,83 +207,106 @@ void TunnelClient::handle_request_and_reply(const json& req) void TunnelClient::run_loop() { - ws_client c; - c.clear_access_channels(websocketpp::log::alevel::all); - c.init_asio(); - - client_ = &c; - - c.set_open_handler( - [this](websocketpp::connection_hdl h) - { - hdl_ = h; - std::printf("[Tunnel] Connected to server\n"); - }); - - c.set_close_handler([this](websocketpp::connection_hdl) { std::printf("[Tunnel] Disconnected from server\n"); }); - - c.set_fail_handler([this](websocketpp::connection_hdl) { std::printf("[Tunnel] Connect failed\n"); }); - - c.set_message_handler( - [this](websocketpp::connection_hdl, ws_client::message_ptr msg) - { - try - { - if (msg->get_opcode() != websocketpp::frame::opcode::text) - { - std::printf("[Tunnel] ignore non-text msg from server\n"); - return; - } - auto payload = msg->get_payload(); - json req = json::parse(payload); - - // 同步处理会阻塞 websocket 线程:这里开线程处理,避免卡住心跳/读写 - std::thread( - [this, req]() - { - try - { - handle_request_and_reply(req); - } - catch (const std::exception& e) - { - std::printf("[Tunnel] handler exception: %s\n", e.what()); - } - catch (...) - { - std::printf("[Tunnel] handler unknown exception\n"); - } - }) - .detach(); - } - catch (const std::exception& e) - { - std::printf("[Tunnel] JSON parse error: %s\n", e.what()); - } - catch (...) - { - std::printf("[Tunnel] JSON parse unknown error\n"); - } - }); - - websocketpp::lib::error_code ec; - auto conn = c.get_connection(ws_url_, ec); - if (ec) - { - std::printf("[Tunnel] Connection init failed: %s\n", ec.message().c_str()); - client_ = nullptr; - return; - } - - c.connect(conn); - - // 用 run():稳定事件循环 while (running_.load()) { + // 每次循环都创建一个新的 client,避免 websocketpp 状态复用崩溃 + ws_client c; + c.clear_access_channels(websocketpp::log::alevel::all); + c.clear_error_channels(websocketpp::log::elevel::all); + + c.init_asio(); + + // (可选但推荐)让 run() 不会因为没有 work 而提前退出 + c.start_perpetual(); + + // 让 stop() 能安全拿到当前 client + { + // 如果你不想引入 mutex,这段也可以不加,但建议加 + client_ = &c; + } + + c.set_open_handler( + [this](websocketpp::connection_hdl h) + { + hdl_ = h; + std::printf("[Tunnel] Connected to server\n"); + }); + + c.set_close_handler( + [this, &c](websocketpp::connection_hdl) + { + std::printf("[Tunnel] Disconnected from server\n"); + // 让 run() 退出,进入下一轮重连 + c.stop_perpetual(); + c.stop(); + }); + + c.set_fail_handler( + [this, &c](websocketpp::connection_hdl) + { + std::printf("[Tunnel] Connect failed\n"); + // 让 run() 退出,进入下一轮重连 + c.stop_perpetual(); + c.stop(); + }); + + c.set_message_handler( + [this](websocketpp::connection_hdl, ws_client::message_ptr msg) + { + try + { + if (msg->get_opcode() != websocketpp::frame::opcode::text) + { + std::printf("[Tunnel] ignore non-text msg from server\n"); + return; + } + json req = json::parse(msg->get_payload()); + + // 你原来的做法:起线程处理本地 HTTP,OK + std::thread( + [this, req]() + { + try + { + handle_request_and_reply(req); + } + catch (const std::exception& e) + { + std::printf("[Tunnel] handler exception: %s\n", e.what()); + } + catch (...) + { + std::printf("[Tunnel] handler unknown exception\n"); + } + }) + .detach(); + } + catch (const std::exception& e) + { + std::printf("[Tunnel] JSON parse error: %s\n", e.what()); + } + catch (...) + { + std::printf("[Tunnel] JSON parse unknown error\n"); + } + }); + + websocketpp::lib::error_code ec; + auto conn = c.get_connection(ws_url_, ec); + if (ec) + { + std::printf("[Tunnel] Connection init failed: %s\n", ec.message().c_str()); + client_ = nullptr; + // 这里别直接 return,继续重试 + goto retry_sleep; + } + + c.connect(conn); + try { + // 阻塞运行,直到 fail/close 里 stop(),或 stop() 主动 close c.run(); - // run() 返回说明 stopped 了,稍等再重连 } catch (const std::exception& e) { @@ -291,25 +317,15 @@ void TunnelClient::run_loop() std::printf("[Tunnel] run unknown exception\n"); } + client_ = nullptr; + if (!running_.load()) break; + retry_sleep: std::printf("[Tunnel] reconnecting in 2s...\n"); std::this_thread::sleep_for(std::chrono::seconds(2)); - - // 重建连接 - c.reset(); - c.init_asio(); - - websocketpp::lib::error_code ec2; - auto conn2 = c.get_connection(ws_url_, ec2); - if (ec2) - { - std::printf("[Tunnel] reconnect init failed: %s\n", ec2.message().c_str()); - continue; - } - c.connect(conn2); } - std::printf("[Tunnel] Loop exit\n"); client_ = nullptr; + std::printf("[Tunnel] Loop exit\n"); }