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

WebSocket空闲超时实现疑问:取消async_read后Socket无法复用

问题

我用timer配合async_read实现WebSocket空闲超时,超时后调用ws_.next_layer().cancel()取消async_read,但后续执行ws_.write()时出现operation cancelled错误。请问:

  • ws_.next_layer().cancel()是否会导致Socket无法用于后续操作?
  • 有没有其他方法可以捕获空闲超时并退出async_read,同时保持Socket可用无需关闭重连?
代码示例
#include "Socket_Factory_1.h"


class TimeoutException : public std::exception {
public:
    explicit TimeoutException(const std::string& message) : message_(message) {}
    
    virtual const char* what() const noexcept override {
        return message_.c_str();
    }

private:
    std::string message_;
};

WebSocketClient::WebSocketClient(const std::string& host, const std::string& port)
    : host_(host), port_(port), ioc_(), resolver_(ioc_), ws_(ioc_), timer_(ioc_) {}

WebSocketClient::~WebSocketClient() {
    close();
}

void WebSocketClient::connect() {
    try {
        ws_.binary(true);
        auto const results = resolver_.resolve(host_, port_);
        asio::connect(ws_.next_layer(), results.begin(), results.end());
        ws_.handshake(host_, "/");
    } catch (const std::exception& e) {
        std::cerr << "Connect Error: " << e.what() << std::endl;
        throw;
    }
}

void WebSocketClient::send(const void* data, size_t size) {
    try {
        ws_.write(asio::buffer(data, size));
    } catch (const std::exception& e) {
        std::cerr << "Send Error: " << e.what() << std::endl;
        throw;
    }
}

std::string WebSocketClient::receive() {
    std::cout<<"HI1";
    
        
        read_successful = false;
        timeout_occurred = false;

        // Set up the timer
        std::chrono::seconds timeout_duration(3);
        timer_.expires_after(timeout_duration);
        std::cout<<"HI";
        // Start an asynchronous read operation
        ws_.async_read(buffer,
            [this](beast::error_code ec, std::size_t bytes_transferred) {
                if (!ec) {
                    result = beast::buffers_to_string(buffer.data());
                    read_successful = true; // Mark read as successful
                } 
                else if(ec==asio::error::operation_aborted){
                    std::cout<<"aborted"<<std::endl;
                }
                else {
                    std::cerr << "Read Error: " << ec.message() << std::endl;
                }
            }
        );

        // Set up the timeout handler
        timer_.async_wait(
            [this](beast::error_code ec) {
                if (ec != asio::error::operation_aborted) {
                    timeout_occurred = true; // Mark timeout as occurred
                }else{

                    std::cout<<"aborted tiemr"<<std::endl;
                }
            }
        );

        // Run the IO context to perform the asynchronous operations
        while (!read_successful && !timeout_occurred) {
            ioc_.run_one(); // Process one ready handler
        }
        buffer.clear();
        if (timeout_occurred) {
            ws_.next_layer().cancel();
            ioc_.run();
            // ioc_.restart();
            std::cerr << "Read operation timed out\n";
            throw TimeoutException("Timeout");
        }
        if (read_successful) {
            timer_.cancel();
            return result;
        }
        return ""; // Return empty string if neither read nor timeout occurred
    }
void WebSocketClient::close() {
    try {
        if (ws_.is_open()) {
            ws_.close(websocket::close_code::normal);
        }
    } catch (const std::exception& e) {
        std::cerr << "Close Error: " << e.what() << std::endl;
    }
}
解决方案

关于ws_.next_layer().cancel()的影响

是的,调用该方法会取消底层Socket上所有未完成的异步操作,包括后续准备执行的write操作。这个操作会让Socket进入不稳定状态,导致后续所有依赖该Socket的操作都会抛出operation cancelled错误,无法继续使用该Socket进行正常通信。

正确的空闲超时实现方式

要实现超时退出async_read且保持Socket可用,应该使用Beast WebSocket流自带的cancel()方法,仅取消读操作,而非底层Socket。具体步骤如下:

1. 使用WebSocket流的定向cancel操作

调用ws_.cancel(websocket::cancel_type::read)可以仅终止当前的异步读操作,不会影响Socket本身,后续仍可执行write或新的read操作。

2. 修正超时逻辑

在定时器回调中直接执行定向cancel,无需通过标志位延迟处理,确保操作及时响应。

3. 优化IO上下文管理

使用局部状态变量(如原子变量)跟踪操作状态,避免成员变量的线程安全问题;重置IO上下文确保残留handler被清理。

修改后的receive函数示例

std::string WebSocketClient::receive() {
    std::cout << "HI1\n";
    
    std::atomic<bool> read_successful{false};
    std::atomic<bool> timeout_occurred{false};
    beast::flat_buffer buffer;
    std::string result;

    // 设置超时定时器
    std::chrono::seconds timeout_duration(3);
    timer_.expires_after(timeout_duration);

    // 启动异步读操作
    ws_.async_read(buffer,
        [this, &read_successful, &result, &buffer](beast::error_code ec, std::size_t bytes_transferred) {
            if (!ec) {
                result = beast::buffers_to_string(buffer.data());
                read_successful = true;
            } else if (ec == asio::error::operation_aborted) {
                std::cout << "read aborted\n";
            } else {
                std::cerr << "Read Error: " << ec.message() << "\n";
            }
        }
    );

    // 超时回调:仅取消WebSocket读操作
    timer_.async_wait(
        [this, &timeout_occurred](beast::error_code ec) {
            if (!ec) {
                beast::error_code cancel_ec;
                // 仅取消读操作,不影响写操作
                ws_.cancel(websocket::cancel_type::read, cancel_ec);
                if (cancel_ec) {
                    std::cerr << "Cancel read error: " << cancel_ec.message() << "\n";
                }
                timeout_occurred = true;
            } else {
                std::cout << "timer aborted\n";
            }
        }
    );

    // 运行IO上下文直到操作完成或超时
    ioc_.reset();
    while (!read_successful && !timeout_occurred) {
        ioc_.run_one();
    }

    // 清理定时器
    beast::error_code cancel_timer_ec;
    timer_.cancel(cancel_timer_ec);

    if (timeout_occurred) {
        std::cerr << "Read operation timed out\n";
        throw TimeoutException("Timeout");
    }

    return read_successful ? result : "";
}

关键说明

  • 避免直接操作底层Socket的cancel方法,优先使用WebSocket流提供的定向cancel接口。
  • 局部原子变量确保多线程环境下的状态正确性(如果存在多线程调用receive的情况)。
  • IO上下文的reset()方法确保每次调用receive时,之前的操作残留不会干扰当前逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:34:58