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

C++基于Poco库将SocketStream转为WebSocket实现JSON报文传输问题

Poco::Net::WebSocket 实现改造方案

问题根因定位

首先明确你当前卡住的两个核心问题:

  • 局部变量生命周期问题:你把HTTPClientSession定义在构造函数内部,构造函数执行完毕后该对象被销毁,WebSocket依赖的底层会话直接失效,即使握手成功后续也无法正常收发数据。
  • 服务端未实现WebSocket握手逻辑:你之前搭建的普通TCPServer没有处理HTTP Upgrade握手请求,客户端发出握手请求后一直等不到响应,就会出现卡住的情况。

WebSocket 核心逻辑说明

WebSocket 确实是基于TCP的全双工协议,但必须经过固定的HTTP升级握手流程才能建立连接:

  1. 客户端向服务端发送带Upgrade: websocket头的HTTP GET请求
  2. 服务端返回101 Switching Protocols响应
  3. 握手完成后,双方才能按WebSocket帧格式收发数据,后续不需要再走HTTP协议

客户端改造代码

第一步:修正类成员变量定义

class ClientHandler
{
private:
    std::string host;
    int port;
    // 把会话、WebSocket都作为类成员,保证生命周期和ClientHandler一致
    Poco::Net::HTTPClientSession cs;
    Poco::Net::WebSocket ws;
public:
    ClientHandler(std::string h, int p): host(h), port(p), cs(h, p)
    {
        // 构造握手请求,必须用HTTP_GET,URI可以随便填,只要服务端对应处理即可
        Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, "/ws", Poco::Net::HTTPMessage::HTTP_1_1);
        Poco::Net::HTTPResponse resp;
        // Poco会自动填充WebSocket握手需要的Header,不需要手动设置
        ws = Poco::Net::WebSocket(cs, req, resp);
        // 验证握手结果
        if (resp.getStatus() != Poco::Net::HTTPResponse::HTTP_SWITCHING_PROTOCOLS) {
            throw Poco::ApplicationException("WebSocket 握手失败");
        }
        std::cout << "Host: " << host << "\tPort: " << port << " 连接成功" << std::endl;
    }

第二步:实现消息发送逻辑

bool sendMessage()
    {
        std::cout << std::endl;
        const char channel[] = "fool";
        std::string channelPort = std::to_string(5577);
        try
        {
            std::string message;
            std::cout << "Enter a message to send to the server: ";
            std::cin >> message;
            if (message == "exit") {
                // 正常关闭WebSocket连接
                ws.shutdown();
                return false;
            }
            json msg = json(json_object_arg, {{"$op", "create_channel"}, {"$svc", "zmq"}, {"channel_name", channel}, {"port", channelPort}});
            std::vector<uint8_t> buffer;
            jsoncons::cbor::encode_cbor(msg, buffer);
            // 发送二进制帧,因为是CBOR编码,用FRAME_BINARY,如果是文本用FRAME_TEXT
            ws.sendBytes(buffer.data(), buffer.size(), Poco::Net::WebSocket::FRAME_BINARY);
            std::cout << "消息发送成功" << std::endl;
            // 可选:接收服务端响应
            char recvBuf[1024] = {0};
            int flags = 0;
            int n = ws.receiveBytes(recvBuf, sizeof(recvBuf), flags);
            if (n > 0) {
                std::cout << "收到服务端响应,长度:" << n << std::endl;
            }
            return true;
        }
        catch (Poco::Exception &err)
        {
            std::cout << "数据发送错误: [" << err.displayText() << "]" << std::endl;
            ws.shutdown();
            return false;
        }
    }
};

主函数不需要修改

服务端实现注意事项

你不能用普通的TCPServer,需要基于Poco的HTTP服务器实现握手处理:

  1. 继承Poco::Net::HTTPRequestHandler实现自己的请求处理类,在handleRequest方法中判断是否是WebSocket升级请求
  2. 如果是升级请求,构造Poco::Net::WebSocket对象,后续用该对象收发数据
  3. 收发帧的时候注意判断帧类型,区分二进制、文本、关闭、 ping/pong帧

常见参数说明

  • 握手请求必须用HTTP_GET,这是WebSocket协议规定的
  • URI你可以自定义,比如用/ws,只要服务端对应路径处理WebSocket请求即可,不需要带多余参数
  • Origin头只有在对接浏览器的时候需要,服务端之间通信不需要设置
  • WebSocket的sendFlags参数:二进制数据用FRAME_BINARY,文本数据用FRAME_TEXT,控制帧会自动处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 07:45:02