在C++中使用Boost.Asio处理多线程WebSocket客户端中的EXC_BAD_ACCESS

cwdobuhd  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(208)

我正在使用Boost.Beast和Boost.Asio开发一个C++多线程WebSocket客户端。我偶尔会在WebSocket握手过程(do_handshake方法)中遇到分段错误(EXC_BAD_ACCESS)。该应用程序旨在处理来自服务器的实时数据流。

下面是我的WebSocketClient类的简化版本:

class WebSocketClient
{
private:
    OrderBook &orderBook_;
    string coin_;

    boost::asio::io_context ioc_;
    tcp::resolver resolver_;
    ssl::context ssl_context_;
    websocket::stream<boost::asio::ssl::stream<tcp::socket>> ws_;

public:
    WebSocketClient(OrderBook &orderBook, const string &coin)
        : orderBook_(orderBook),
          coin_(coin),
          resolver_(ioc_),
          ssl_context_(ssl::context::tlsv12_client),
          ws_(ioc_, ssl_context_)
    {
        ssl_context_.set_default_verify_paths();
        ws_.next_layer().set_verify_mode(ssl::verify_peer);
        ws_.next_layer().set_verify_callback(ssl::rfc2818_verification("stream.binance.com"));
    }

    void connectAndSubscribe()
    {
        try
        {
            cout << "&ws_ = " << &ws_ << endl;
            // Resolve the host name and connect to the server
            auto const results = resolver_.resolve("stream.binance.com", "9443");
            auto ep = boost::asio::connect(ws_.next_layer().next_layer(), results);
            string host_ = "stream.binance.com:" + to_string(ep.port());

            ws_.next_layer().handshake(ssl::stream_base::client);
            // Set a decorator to change the User-Agent of the handshake
            ws_.set_option(websocket::stream_base::decorator(
                [](websocket::request_type &req)
                {
                    req.set(boost::beast::http::field::user_agent,
                            string(BOOST_BEAST_VERSION_STRING) + " websocket-client");
                }));

            string path = "/ws/" + coin_ + "@depth5@100ms";
            ws_.handshake(host_, path);
            cout << "Handshake successful." << endl;

            string msg = "{\"method\": \"SUBSCRIBE\", \"params\": [\"" + coin_ + "@depth5@100ms\"], \"id\": 1}";
            send(msg);
            cout << "Subscription request done!" << endl;
        }
        catch (const std::exception &e)
        {
            cout << "Error in connectAndSubscribe: " << e.what() << endl;
            throw; // Rethrow to be caught by the caller
        }
    }

字符串
该类用于多线程上下文中,其中每个线程创建一个WebSocketClient示例来处理其连接。崩溃偶尔发生,堆栈跟踪指向do_handshake方法。
我已经检查了对象的生存期,像ssl_context_、resolver_和ws_这样的对象是WebSocketClient类的成员。我确保OrderBook对象的生存期超过WebSocketClient示例。

lldb终端:

* thread #5, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010003a28b adabtc`void boost::beast::websocket::stream<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >, true>::do_handshake<void (*)(boost::beast::http::message<true, boost::beast::http::empty_body, boost::beast::http::basic_fields<std::__1::allocator<char> > >&)>(boost::beast::http::message<false, boost::beast::http::basic_string_body<char, std::__1::char_traits<char>, std::__1::allocator<char> >, boost::beast::http::basic_fields<std::__1::allocator<char> > >*, boost::core::basic_string_view<char>, boost::core::basic_string_view<char>, void (* const&)(boost::beast::http::message<true, boost::beast::http::empty_body, boost::beast::http::basic_fields<std::__1::allocator<char> > >&), boost::system::error_code&) + 843
adabtc`boost::beast::websocket::stream<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >, true>::do_handshake<void (*)(boost::beast::http::message<true, boost::beast::http::empty_body, boost::beast::http::basic_fields<std::__1::allocator<char> > >&)>:
->  0x10003a28b <+843>: vmovaps 0x40(%rax), %ymm0
    0x10003a290 <+848>: leaq   0x2210(%rsp), %rax
    0x10003a298 <+856>: vmovups %ymm0, (%rax)
    0x10003a29c <+860>: movq   %r13, %r12
Target 0: (adabtc) stopped.

zzoitvuj

zzoitvuj1#

这不是一个C++异常。这是一般的保护错误。你的工作不是处理它们,而是避免它们。很明显,你有UB,很可能是数据竞争。修复它,或者发布一个自包含的例子,以便我们可以审查。这是我从你的代码开始-这是我现在所有的20分钟:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/json.hpp>
#include <iostream>

namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace http      = boost::beast::http;
namespace websocket = boost::beast::websocket;
using net::ip::tcp;
struct OrderBook {
};

struct WebSocketClient {
    using Socket = tcp::socket;
    using Stream = ssl::stream<Socket>;
    using WS     = websocket::stream<Stream>;

    net::io_context ioc_;
    OrderBook&      orderBook_;
    std::string     coin_;
    tcp::resolver   resolver_{ioc_};
    ssl::context    ssl_context_{ssl::context::tlsv12_client};
    WS              ws_{ioc_, ssl_context_};

    WebSocketClient(OrderBook& orderBook, std::string coin) : orderBook_(orderBook), coin_(std::move(coin)) {
        ssl_context_.set_default_verify_paths();
        ws_.next_layer().set_verify_mode(ssl::verify_peer);
        ws_.next_layer().set_verify_callback(ssl::rfc2818_verification("stream.binance.com"));
    }

    void send(std::string msg) {
        ws_.write(net::buffer(msg));
    }

    void connectAndSubscribe() {
        // std::cout << "&ws_ = " << &ws_ << std::endl;

        // Resolve the host name and connect to the server
        auto results = resolver_.resolve("stream.binance.com", "9443");
        auto ep      = connect(ws_.next_layer().next_layer(), results);
        auto host_   = "stream.binance.com:" + std::to_string(ep.port());

        ws_.next_layer().handshake(Stream::client);
        // Set a decorator to change the User-Agent of the handshake
        ws_.set_option(websocket::stream_base::decorator([](websocket::request_type& req) {
            req.set(http::field::user_agent, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-client");
        }));

        std::string path = "/ws/" + coin_ + "@depth5@100ms";
        ws_.handshake(host_, path);
        std::cout << "Handshake successful." << std::endl;

        // std::string msg = R"({"method": "SUBSCRIBE", "params": [")" + coin_ + R"(@depth5@100ms"], "id": 1})";
        std::string msg = serialize(boost::json::value{
            {"method", "SUBSCRIBE"},
            {"params", {coin_ + "@depth5@100ms"}},
            {"id", 1},
        });
        // msg = R"({"method": "SUBSCRIBE", "params": [")" + coin_ + R"(@depth5@100ms"], "id": 1})";

        send(msg);
        std::cout << "Subscription request done!" << std::endl;
    }
};

int main() {
    OrderBook ob;
    WebSocketClient x(ob, "USD");
    x.connectAndSubscribe();
}

字符串
在我的机器上打印出预期的

Handshake successful.
Subscription request done!


该应用程序旨在处理来自服务器的实时数据流
我非常怀疑它,因为我希望线程感知和异步IO。
请扩展最小的自包含代码来重现您的错误。或者简单地展示如何组织阅读以及如何在订单簿上完成线程同步。

相关问题