C++基于Poco库将SocketStream转为WebSocket实现JSON报文传输问题
Poco::Net::WebSocket 实现改造方案
问题根因定位
首先明确你当前卡住的两个核心问题:
- 局部变量生命周期问题:你把
HTTPClientSession定义在构造函数内部,构造函数执行完毕后该对象被销毁,WebSocket依赖的底层会话直接失效,即使握手成功后续也无法正常收发数据。 - 服务端未实现WebSocket握手逻辑:你之前搭建的普通
TCPServer没有处理HTTP Upgrade握手请求,客户端发出握手请求后一直等不到响应,就会出现卡住的情况。
WebSocket 核心逻辑说明
WebSocket 确实是基于TCP的全双工协议,但必须经过固定的HTTP升级握手流程才能建立连接:
- 客户端向服务端发送带
Upgrade: websocket头的HTTP GET请求 - 服务端返回101 Switching Protocols响应
- 握手完成后,双方才能按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服务器实现握手处理:
- 继承
Poco::Net::HTTPRequestHandler实现自己的请求处理类,在handleRequest方法中判断是否是WebSocket升级请求 - 如果是升级请求,构造
Poco::Net::WebSocket对象,后续用该对象收发数据 - 收发帧的时候注意判断帧类型,区分二进制、文本、关闭、 ping/pong帧
常见参数说明
- 握手请求必须用
HTTP_GET,这是WebSocket协议规定的 - URI你可以自定义,比如用
/ws,只要服务端对应路径处理WebSocket请求即可,不需要带多余参数 - Origin头只有在对接浏览器的时候需要,服务端之间通信不需要设置
- WebSocket的sendFlags参数:二进制数据用
FRAME_BINARY,文本数据用FRAME_TEXT,控制帧会自动处理
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

