试图用 Boost::Beast 替换我的 libwebsocket 代码

Posted

技术标签:

【中文标题】试图用 Boost::Beast 替换我的 libwebsocket 代码【英文标题】:Trying to replace my libwebsocket code with Boost::Beast 【发布时间】:2017-10-13 09:36:00 【问题描述】:

我正在使用 libwebsockets 使用 C++ 程序连接到网络服务器。我能够连接。但是我如何在 Boost::Beast 中做到这一点

// Setup our lws connection info
        struct lws_client_connect_info ConnectInfo;
        memset(&ConnectInfo, 0, sizeof(ConnectInfo));
        ConnectInfo.context = Context;
        ConnectInfo.address = Host.c_str();
        ConnectInfo.port = Port;
        ConnectInfo.path = Path.c_str();
        ConnectInfo.origin = NULL;
        ConnectInfo.protocol = Protocols[1].name;
        ConnectInfo.ietf_version_or_minus_one = -1;
        ConnectInfo.ssl_connection = UseSSL ? 1 : 0; // XXX: If you want to allow self-signed certs, change 1 to 2

        std::string fullhost;
        fullhost.append(Host).append(":").append(std::to_string(Port));
        ConnectInfo.host = fullhost.c_str();

        if (lws_client_connect_via_info(&ConnectInfo) == nullptr) 
            Logger::Error("Unable to initialize connection!");
            return;
        

我试过了,但握手不起作用。删除握手会导致进一步的错误。这是直接来自 Boost::Beast 文档的简单 websocket 同步调用(为了简单和测试)sn-p。这里有没有办法,会有什么变化和语法?

// The io_service is required for all I/O
        boost::asio::io_service ios;

        // These objects perform our I/O
        tcp::resolver resolver ios ;
        websocket::stream<tcp::socket> ws ios ;

        // Look up the domain name
        auto const lookup = resolver.resolve( host, port );

        // Make the connection on the IP address we get from a lookup
        boost::asio::connect(ws.next_layer(), lookup);

        // Perform the websocket handshake
        ws.handshake(host, "/");

        // Send the message
        ws.write(boost::asio::buffer(std::string(text)));

        // This buffer will hold the incoming message
        boost::beast::multi_buffer buffer;

        // Read a message into our buffer
        ws.read(buffer);

        // Close the WebSocket connection
        ws.close(websocket::close_code::normal);

        // If we get here then the connection is closed gracefully

        // The buffers() function helps print a ConstBufferSequence
        std::cout << boost::beast::buffers(buffer.data()) << std::endl;

但是当 libwebconnect 的代码被清除时,beast 在握手请求中给出了一个错误。连接通过。

这一行显示:

std::cerr << "Error: " << e.what() << std::endl;

错误:Websocket 升级握手失败。

是否需要进行其他一些设置才能使其正常工作?我发现同步和异步示例都遵循相同的初始设置,并且我都尝试过使用野兽示例。

10/15 更新:我决定更深入地研究 libwebsocket 所做的代码,这是我发现的,我希望这会有所帮助。

/**
 * lws_client_connect_via_info() - Connect to another websocket server
 * \param ccinfo: pointer to lws_client_connect_info struct
 *
 *  This function creates a connection to a remote server using the
 *  information provided in ccinfo.
 */
LWS_VISIBLE LWS_EXTERN struct lws *
lws_client_connect_via_info(struct lws_client_connect_info * ccinfo);

实施

LWS_VISIBLE struct lws *
lws_client_connect_via_info(struct lws_client_connect_info *i)

    struct lws *wsi;
    int v = SPEC_LATEST_SUPPORTED;
    const struct lws_protocols *p;

    if (i->context->requested_kill)
        return NULL;

    if (!i->context->protocol_init_done)
        lws_protocol_init(i->context);

    wsi = lws_zalloc(sizeof(struct lws), "client wsi");
    if (wsi == NULL)
        goto bail;

    wsi->context = i->context;
    /* assert the mode and union status (hdr) clearly */
    lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
    wsi->desc.sockfd = LWS_SOCK_INVALID;

    /* 1) fill up the wsi with stuff from the connect_info as far as it
     * can go.  It's because not only is our connection async, we might
     * not even be able to get ahold of an ah at this point.
     */

    /* -1 means just use latest supported */
    if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
        v = i->ietf_version_or_minus_one;

    wsi->ietf_spec_revision = v;
    wsi->user_space = NULL;
    wsi->state = LWSS_CLIENT_UNCONNECTED;
    wsi->pending_timeout = NO_PENDING_TIMEOUT;
    wsi->position_in_fds_table = -1;
    wsi->c_port = i->port;
    wsi->vhost = i->vhost;
    if (!wsi->vhost)
        wsi->vhost = i->context->vhost_list;

    wsi->protocol = &wsi->vhost->protocols[0];

    /* for http[s] connection, allow protocol selection by name */

    if (i->method && i->vhost && i->protocol) 
        p = lws_vhost_name_to_protocol(i->vhost, i->protocol);
        if (p)
            wsi->protocol = p;
    

    if (wsi && !wsi->user_space && i->userdata) 
        wsi->user_space_externally_allocated = 1;
        wsi->user_space = i->userdata;
     else
        /* if we stay in http, we can assign the user space now,
         * otherwise do it after the protocol negotiated
         */
        if (i->method)
            if (lws_ensure_user_space(wsi))
                goto bail;

#ifdef LWS_OPENSSL_SUPPORT
    wsi->use_ssl = i->ssl_connection;
#else
    if (i->ssl_connection) 
        lwsl_err("libwebsockets not configured for ssl\n");
        goto bail;
    
#endif

    /* 2) stash the things from connect_info that we can't process without
     * an ah.  Because if no ah, we will go on the ah waiting list and
     * process those things later (after the connect_info and maybe the
     * things pointed to have gone out of scope.
     */

    wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash");
    if (!wsi->u.hdr.stash) 
        lwsl_err("%s: OOM\n", __func__);
        goto bail;
    

    wsi->u.hdr.stash->origin[0] = '\0';
    wsi->u.hdr.stash->protocol[0] = '\0';
    wsi->u.hdr.stash->method[0] = '\0';
    wsi->u.hdr.stash->iface[0] = '\0';

    strncpy(wsi->u.hdr.stash->address, i->address,
        sizeof(wsi->u.hdr.stash->address) - 1);
    strncpy(wsi->u.hdr.stash->path, i->path,
        sizeof(wsi->u.hdr.stash->path) - 1);
    strncpy(wsi->u.hdr.stash->host, i->host,
        sizeof(wsi->u.hdr.stash->host) - 1);
    if (i->origin)
        strncpy(wsi->u.hdr.stash->origin, i->origin,
            sizeof(wsi->u.hdr.stash->origin) - 1);
    if (i->protocol)
        strncpy(wsi->u.hdr.stash->protocol, i->protocol,
            sizeof(wsi->u.hdr.stash->protocol) - 1);
    if (i->method)
        strncpy(wsi->u.hdr.stash->method, i->method,
            sizeof(wsi->u.hdr.stash->method) - 1);
    if (i->iface)
        strncpy(wsi->u.hdr.stash->iface, i->iface,
            sizeof(wsi->u.hdr.stash->iface) - 1);

    wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0';
    wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0';
    wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0';
    wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0';
    wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0';
    wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0';
    wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0';

    if (i->pwsi)
        *i->pwsi = wsi;

    /* if we went on the waiting list, no probs just return the wsi
     * when we get the ah, now or later, he will call
     * lws_client_connect_via_info2() below.
     */
    if (lws_header_table_attach(wsi, 0) < 0) 
        /*
         * if we failed here, the connection is already closed
         * and freed.
         */
        goto bail1;
    

    if (i->parent_wsi) 
        lwsl_info("%s: created child %p of parent %p\n", __func__,
                wsi, i->parent_wsi);
        wsi->parent = i->parent_wsi;
        wsi->sibling_list = i->parent_wsi->child_list;
        i->parent_wsi->child_list = wsi;
    
#ifdef LWS_WITH_HTTP_PROXY
    if (i->uri_replace_to)
        wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
                         i->uri_replace_from,
                         i->uri_replace_to);
#endif

    return wsi;

bail:
    lws_free(wsi);

bail1:
    if (i->pwsi)
        *i->pwsi = NULL;

    return NULL;

【问题讨论】:

您连接的服务器端口是否需要 SSL? 不,我正在本地测试它。这是不安全的。但为了它,我已经尝试过使用 ssl/nossl 进行同步/异步 【参考方案1】:

我发现了问题/问题。我看到的握手的一般错误/异常让我很长时间才弄清楚。

    问题很愚蠢,我在握手时没有给它正确的路径

/api?serverkey=defaultkey&token=eyJhbGciOiJIUzI1NiIsInR5

    服务器有一个超时设置,这将关闭 WebSocket 连接。

我在想为什么错误消息不是很具有描述性。请原谅我的无知,但是为了向客户端发送正确的错误消息,例如会话过期/没有有效凭据等,是否应该检查 boost asio/beast 或本地服务器?

【讨论】:

如果您想检查 HTTP 响应,您必须使用 beast::webocket::stream::handshake 的重载,它将响应对象存储在 out-param 中。比如这个:boost.org/doc/libs/master/libs/beast/doc/html/beast/ref/…

以上是关于试图用 Boost::Beast 替换我的 libwebsocket 代码的主要内容,如果未能解决你的问题,请参考以下文章

如何正确写c++ boost beast websocket server

Boost Beast服务器响应延迟1秒

C++ Boost 1.66 使用 Beast http request Parser 来解析字符串

使用 boost::beast 处理大型 http 响应

Boost.Beast 高级服务器示例中的 HTTP Pipelining vs. WebSocket

Boost :: Beast Websocket双向流(C ++)