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

使用WebSocket++连接Render远程服务器失败,本地及Qt客户端正常

WebSocket++客户端无法连接Render.com上的WebSocket服务器问题排查

环境

  • Windows 10 + MinGW 64-bit
  • WebSocket++ 0.8.2
  • ASIO 1.24.0
  • 服务器部署在Render.com,地址:wss://connection-js.onrender.com

遇到的问题

  1. 使用wss://协议时,报错:could not create connection because: endpoint not secure
  2. 使用ws://协议时,报错:
[2022-10-07 00:33:09] [connect] Successful connection
[2022-10-07 00:33:09] [error] Server handshake response error: websocketpp.processor:20 (Invalid HTTP status.)
[2022-10-07 00:33:09] [fail] WebSocket Connection 216.24.57.3:80 - "WebSocket++/0.8.2" / 301 websocketpp.processor:20 Invalid HTTP status.
  • 本地连接ws://localhost:3000正常;Qt6客户端使用wss://可正常连接该远程服务器,但ws://不行。

相关代码与配置

初始客户端main.cpp

#define ASIO_STANDALONE
#define _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_STRICT_
#define _WEBSOCKETPP_CPP11_TYPE_TRAITS_

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

typedef websocketpp::client<websocketpp::config::asio_client> client;

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

// pull out the type of messages sent by our config
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;

// This message handler will be invoked once for each incoming message. It
// prints the message and then sends a copy of the message back to the server.
void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) {
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;

    websocketpp::lib::error_code ec;

    c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
    if (ec) {
        std::cout << "Echo failed because: " << ec.message() << std::endl;
    }
}

int main()
{
    // Create a client endpoint
    client c;

    // std::string uri = "ws://localhost:3000";
    std::string uri = "wss://connection-js.onrender.com";

    try
    {
        // Set logging to be pretty verbose (everything except message payloads)
        c.set_access_channels(websocketpp::log::alevel::all);
        c.clear_access_channels(websocketpp::log::alevel::frame_payload);

        // Initialize ASIO
        c.init_asio();

        // Register our message handler
        c.set_message_handler(bind(&on_message,&c,::_1,::_2));

        websocketpp::lib::error_code ec;
        client::connection_ptr con = c.get_connection(uri, ec);
        if (ec)
        {
            std::cout << "could not create connection because: " << ec.message() << std::endl;
            return 0;
        }

        // Note that connect here only requests a connection. No network messages are
        // exchanged until the event loop starts running in the next line.
        c.connect(con);

        // Start the ASIO io_service run loop
        // this will cause a single connection to be made to the server. c.run()
        // will exit when this connection is closed.
        c.run();
    } catch (websocketpp::exception const & e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

初始makefile

CC = g++

INC = -I"E:\Libs\websocketpp-0.8.2-headers" \
      -I"E:\Libs\asio-1.24.0\include"

all: main.o
    $(CC) main.o -lws2_32 -o app

main.o: main.cpp
    $(CC) -c $(INC) main.cpp -o main.o

服务端app.js

const express = require("express");
const http = require("http");
const ws = require("ws");
const path = require("path");

const app = express();
app.use(express.static(path.join(process.cwd(), "public")));

const httpServer = http.createServer(app);
const wss = new ws.Server(
{
    server: httpServer
});

const port = process.env.PORT || 3000;
httpServer.listen(port, () => console.log("Listening at port: " + port));

wss.on("connection", socket =>
{
    console.log("client was connected");
});

可正常连接的Qt6客户端main.cpp

#include <QtCore/QDebug>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QtWebSockets/QWebSocket>

class Window : public QWidget
{
    Q_OBJECT

private:
    QWebSocket m_webSocket;

public:
    Window(QWidget *parent = nullptr)
        : QWidget(parent)
    {
        setWindowTitle("Qt6 C++ Client");
        resize(300, 300);

        connect(&m_webSocket, &QWebSocket::connected,
            this, &Window::onConnected);
        // m_webSocket.open(QUrl("ws://localhost:3000"));
        m_webSocket.open(QUrl("wss://connection-js.onrender.com"));
        qDebug() << "Waiting for connection...";
    }

private slots:

    void onConnected()
    {
        qDebug() << "Client was connected to server";
    }
};

#include "main.moc"

#ifdef _WIN32
#include <Windows.h>
#endif

int main(int argc, char *argv[])
{
#ifdef _WIN32
    if (AttachConsole(ATTACH_PARENT_PROCESS))
    {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);
    }
#endif

    QApplication a(argc, argv);
    Window w;
    w.show();
    return a.exec();
}

Qt6客户端.pro文件

# Build commands for CMD:
# qmake -makefile
# mingw32-make
# Command to run: "./release/app"
 
QT += core gui widgets websockets

CONFIG += c++11
CONFIG += console
 
SOURCES += \
    main.cpp
 
TARGET = app

尝试添加OpenSSL后的配置与代码

修改后的makefile

# Building Command: mingw32-make
# Command to run: app

CC = g++

INC = -I"E:\Libs\websocketpp-0.8.2-headers" \
      -I"E:\Libs\asio-1.24.0\include" \
      -I"E:\Libs\openssl-master\include"

LIB = -L"E:\Libs\openssl-master\lib\mingw\x64"

all: main.o
    $(CC) main.o $(LIB) -lssl -lcrypto -lws2_32 -o app

main.o: main.cpp
    $(CC) -c $(INC) main.cpp -o main.o

修改后的main.cpp

#define ASIO_STANDALONE
#define _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_STRICT_
#define _WEBSOCKETPP_CPP11_TYPE_TRAITS_

#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>

// typedef websocketpp::client<websocketpp::config::asio_client> client;
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;

using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

// pull out the type of messages sent by our config
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;

context_ptr on_tls_init(const char *hostname, websocketpp::connection_hdl)
{
    // context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);

    // return ctx;
}

// This message handler will be invoked once for each incoming message. It
// prints the message and then sends a copy of the message back to the server.
void on_message(client *c, websocketpp::connection_hdl hdl, message_ptr msg)
{
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;

    websocketpp::lib::error_code ec;

    c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
    if (ec)
    {
        std::cout << "Echo failed because: " << ec.message() << std::endl;
    }
}

int main()
{
    // Create a client endpoint
    client c;

    std::string hostname = "connection-js.onrender.com";
    std::string uri = "ws://" + hostname;

    try
    {
        // Set logging to be pretty verbose (everything except message payloads)
        c.set_access_channels(websocketpp::log::alevel::all);
        c.clear_access_channels(websocketpp::log::alevel::frame_payload);

        // Initialize ASIO
        c.init_asio();

        // Register our message handler
        c.set_message_handler(bind(&on_message, &c, ::_1, ::_2));
        c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));

        websocketpp::lib::error_code ec;
        client::connection_ptr con = c.get_connection(uri, ec);
        if (ec)
        {
            std::cout << "could not create connection because: " << ec.message() << std::endl;
            return 0;
        }

        // Note that connect here only requests a connection. No network messages are
        // exchanged until the event loop starts running in the next line.
        c.connect(con);

        // Start the ASIO io_service run loop
        // this will cause a single connection to be made to the server. c.run()
        // will exit when this connection is closed.
        c.run();
    }
    catch (websocketpp::exception const &e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

问题原因与解决方案

问题1:wss://协议报错endpoint not secure

初始客户端使用的asio_no_tls_client配置不支持TLS加密,无法处理wss://类型的请求,必须切换到支持TLS的asio_tls_client配置,同时完整实现TLS上下文初始化逻辑。

问题2:ws://协议返回301错误

Render.com平台默认会将所有HTTP请求(包括ws://)重定向到HTTPS(wss://),而WebSocket++默认不处理HTTP重定向,导致握手时收到301响应后直接失败。

最终修复步骤

  1. 使用正确的TLS客户端配置

    • 将客户端类型替换为websocketpp::client<websocketpp::config::asio_tls_client>
    • 实现完整的TLS初始化函数,不能留空:
      context_ptr on_tls_init(const char *hostname, websocketpp::connection_hdl) {
          context_ptr ctx = websocketpp::lib::make_shared<websocketpp::lib::asio::ssl::context>(websocketpp::lib::asio::ssl::context::tlsv12_client);
          try {
              // 禁用不安全的SSL/TLS版本,只保留安全的TLS 1.2+
              ctx->set_options(websocketpp::lib::asio::ssl::context::default_workarounds |
                               websocketpp::lib::asio::ssl::context::no_sslv2 |
                               websocketpp::lib::asio::ssl::context::no_sslv3 |
                               websocketpp::lib::asio::ssl::context::no_tlsv1 |
                               websocketpp::lib::asio::ssl::context::no_tlsv1_1);
              // 设置SNI(服务器名称指示),适配现代HTTPS服务器
              SSL_CTX_set_tlsext_host_name(ctx->native_handle(), hostname);
          } catch (std::exception& e) {
              std::cout << "TLS init error: " << e.what() << std::endl;
          }
          return ctx;
      }
      
  2. 仅使用wss://协议连接
    由于Render.com强制HTTP重定向到HTTPS,客户端必须直接使用wss://connection-js.onrender.com,不要尝试ws://协议。

  3. 修正代码中的URI与配置

    • 在main函数中设置正确的wss URI:std::string uri = "wss://connection-js.onrender.com";
    • 确保注册TLS初始化处理器:c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));
  4. 编译时链接正确的库
    确保makefile中包含OpenSSL的头文件和库路径,链接-lssl -lcrypto -lws2_32,同时运行时要将OpenSSL的DLL文件(如libssl-1_1-x64.dll、libcrypto-1_1-x64.dll)放在可执行文件目录或系统PATH中。

修复后的完整客户端main.cpp示例

#define ASIO_STANDALONE
#define _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_STRICT_
#define _WEBSOCKETPP_CPP11_TYPE_TRAITS_

#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_tls_client.hpp>
#include <asio/ssl.hpp>

typedef websocketpp::client<websocketpp::config::asio_tls_client> client;

using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

typedef websocketpp::config::asio_tls_client::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;

context_ptr on_tls_init(const char *hostname, websocketpp::connection_hdl) {
    context_ptr ctx = websocketpp::lib::make_shared<websocketpp::lib::asio::ssl::context>(websocketpp::lib::asio::ssl::context::tlsv12_client);
    try {
        ctx->set_options(websocketpp::lib::asio::ssl::context::default_workarounds |
                         websocketpp::lib::asio::ssl::context::no_sslv2 |
                         websocketpp::lib::asio::ssl::context::no_sslv3 |
                         websocketpp::lib::asio::ssl::context::no_tlsv1 |
                         websocketpp::lib::asio::ssl::context::no_tlsv1_1);
        SSL_CTX_set_tlsext_host_name(ctx->native_handle(), hostname);
    } catch (std::exception& e) {
        std::cout << "TLS init error: " << e.what() << std::endl;
    }
    return ctx;
}

void on_message(client *c, websocketpp::connection_hdl hdl, message_ptr msg) {
    std::cout << "Received message: " << msg->get_payload() << std::endl;
    websocketpp::lib::error_code ec;
    c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
    if (ec) {
        std::cout << "Send failed: " << ec.message() << std::endl;
    }
}

void on_open(client* c, websocketpp::connection_hdl hdl) {
    std::cout << "Connected to server!" << std::endl;
    c->send(hdl, "Hello from WebSocket++ client!", websocketpp::frame::opcode::text);
}

int main() {
    client c;
    std::string hostname = "connection-js.onrender.com";
    std::string uri = "wss://"
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:55:14