当前位置: 首页 > news >正文

websocketpp 安装及使用

介绍

WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的消息推送机制。
  • 传统的 web 程序都是属于 "一问一答" 的形式,即客户端给服务器发送了一个 HTTP 请求,服务器给客户端返回一个 HTTP 响应。这种情况下服务器是属于被动的一方,如果客户端不主动发起请求服务器就无法主动给客户端响应。
  • 像网页即时聊天这样的程序都是非常依赖 "消息推送" 的,即需要服务器主动推动消息到客户端。如果只是使用原生的 HTTP 协议,要想实现消息推送一般需要通过 "轮询" 的方式实现, 而轮询的成本比较高并且也不能及时的获取到消息的响应。
基于上述两个问题, 就产生了 WebSocket 协议,旨在实现客户端与服务器之间的 全双工、持久化通信 。WebSocket 更接近于 TCP 这种级别的通信方式,一旦连接建立完成客户端或者服务器都可以主动的向对方发送数据

与 HTTP 对比

特性WebSocketHTTP
连接方式持久化长连接短连接(请求-响应)
通信模式全双工半双工(客户端发起)
头部开销小(帧格式)大(每次携带完整头)
适用场景实时交互静态资源获取

核心特性

  1. 全双工通信:客户端和服务器可以同时双向发送数据,无需等待请求-响应周期。

  2. 低延迟:建立连接后,数据可以即时传输,无需重复建立连接(HTTP 每次请求需握手)。

  3. 轻量级协议:数据帧头部开销小(最小仅 2 字节),适合高频通信。

  4. 基于 TCP:工作在 OSI 模型的传输层之上,依赖 TCP 的可靠性。

原理解析

WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,通过这个附加头信息完成握手过程并升级协议的过程。
  • HTTP 升级请求(客户端发起)

客户端发送一个包含 Upgrade: websocket 头的 HTTP 请求,其中 Sec-WebSocket-Key 是随机生成的 Base64 字符串,用于安全校验:

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

  • 服务器响应切换协议

服务器返回 HTTP 101 Switching Protocols 表示协议升级成功,其中 Sec-WebSocket-Accept 是客户端 Key 与固定 GUID 拼接后通过 SHA-1 哈希生成的,用于验证握手合法性:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

  • 连接建立

此后,通信转为 WebSocket 协议,数据以帧(Frame)形式传输。

数据帧格式

WebSocket 数据帧包含以下关键字段(二进制格式):

报文字段比较多,我们重点关注这几个字段 :
  • FIN: WebSocket 传输数据以消息为概念单位,一个消息有可能由一个或多个帧组成,FIN 字段为 1 表示末尾帧。
  • RSV1~3:保留字段,只在扩展时使用,若未启用扩展则应置 1,若收到不全为 0 的数据帧,且未协商扩展则立即终止连接。
  • opcode: 标志当前数据帧的类型
  1. 0x0: 表示这是个延续帧,当 opcode 0 表示本次数据传输采用了数据分片,当前收到的帧为其中一个分片
  2. 0x1: 表示这是文本帧
  3. 0x2: 表示这是二进制帧
  4. 0x3-0x7: 保留,暂未使用
  5. 0x8: 表示连接断开
  6. 0x9: 表示 ping
  7. 0xa: 表示 pong
  8. 0xb-0xf: 保留,暂未使用
  • mask:表示 Payload 数据是否被编码,若为 1 则必有 Mask-Key,用于解码 Payload 数据。仅客户端发送给服务端的消息需要设置。
  • Payload length:数据载荷的长度,单位是字节, 有可能为 7 位、7+16 位、7+64
  • 位。假设 Payload length = x:
  1. x 0~126:数据的长度为 x 字节
  2. x 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度
  3. x 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度
  • Mask-Key:当 mask 1 时存在,长度为 4 字节,解码规则: DECODED[i] = ENCODED[i] ^ MASK[i % 4]
  • Payload data: 报文携带的载荷数据

Websocketpp 介绍

WebSocketpp 是一个跨平台的开源头部专用 C++ 库,它实现了 RFC6455( WebSocket 协议)和 RFC7692 WebSocketCompression Extensions )。它允许将 WebSocket 客户端和服务器功能集成到 C++ 程序中。在最常见的配置中,全功能网络 I/O Asio 网络库提供。
WebSocketpp 的主要特性包括:
  • 事件驱动的接口
  • 支持 HTTP/HTTPSWS/WSSIPv6
  • 灵活的依赖管理 — Boost /C++11 标准库
  • 可移植性:Posix/Windows32/64bitIntel/ARM
  • 线程安全
下面是该项目的一些常用网站。
  • githubhttps://github.com/zaphoyd/websocketpp
  • 用户手册: http://docs.websocketpp.org/
  • 官网:http://www.zaphoyd.com/websocketpp

安装

sudo apt-get install libboost-dev libboost-system-dev libwebsocketpp-dev

安装完毕后,若在 /usr/include 下有了 websocketpp 目录就表示安装成功了。

websocketpp 常用接口

namespace websocketpp
{typedef lib::weak_ptr<void> connection_hdl;template <typename config>class endpoint : public config::socket_type{typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;typedef typename connection_type::ptr connection_ptr;typedef typename connection_type::message_ptr message_ptr;typedef lib::function<void(connection_hdl)> open_handler;typedef lib::function<void(connection_hdl)> close_handler;typedef lib::function<void(connection_hdl)> http_handler;typedef lib::function<void(connection_hdl, message_ptr)>message_handler;/* websocketpp::log::alevel::none 禁止打印所有日志*/void set_access_channels(log::level channels);   /*设置日志打印等级*/void clear_access_channels(log::level channels); /*清除指定等级的日志*//*设置指定事件的回调函数*/void set_open_handler(open_handler h);       /*websocket 握手成功回调处理函数*/void set_close_handler(close_handler h);     /*websocket 连接关闭回调处理函数*/void set_message_handler(message_handler h); /*websocket 消息回调处理函数*/void set_http_handler(http_handler h);       /*http 请求回调处理函数*//*发送数据接口*/void send(connection_hdl hdl, std::string &payload, frame::opcode::value op);void send(connection_hdl hdl, void *payload, size_t len, frame::opcode::value op);/*关闭连接接口*/void close(connection_hdl hdl, close::status::value code, std::string &reason);/*获取 connection_hdl 对应连接的 connection_ptr*/connection_ptr get_con_from_hdl(connection_hdl hdl);/*websocketpp 基于 asio 框架实现,init_asio 用于初始化 asio 框架中的 io_service 调度器*/void init_asio();/*设置是否启用地址重用*/void set_reuse_addr(bool value);/*设置 endpoint 的绑定监听端口*/void listen(uint16_t port);/*对 io_service 对象的 run 接口封装,用于启动服务器*/std::size_t run();/*websocketpp 提供的定时器,以毫秒为单位*/timer_ptr set_timer(long duration, timer_handler callback);};template <typename config>class server : public endpoint<connection<config>, config>{/*初始化并启动服务端监听连接的 accept 事件处理*/void start_accept();};template <typename config>class connection: public config::transport_type::transport_con_type,public config::connection_base{/*发送数据接口*/error_code send(std::string &payload, frame::opcode::value op = frame::opcode::text);/*获取 http 请求头部*/std::string const &get_request_header(std::string const &key);/*获取请求正文*/std::string const &get_request_body();/*设置响应状态码*/void set_status(http::status_code::value code);/*设置 http 响应正文*/void set_body(std::string const &value);/*添加 http 响应头部字段*/void append_header(std::string const &key, std::string const &val);/*获取 http 请求对象*/request_type const &get_request();/*获取 connection_ptr 对应的 connection_hdl */connection_hdl get_handle();};namespace http{namespace parser{class parser{std::string const &get_header(std::string const &key);std::string const &get_body();typedef std::map<std::string, std::string,utility::ci_less> header_list;header_list const &get_headers();};class request : public parser{/*获取请求方法*/std::string const &get_method()/*获取请求 uri 接口*/std::string const &get_uri()};}};namespace message_buffer{/*获取 websocket 请求中的 payload 数据类型*/frame::opcode::value get_opcode();/*获取 websocket 中 payload 数据*/std::string const &get_payload();};
}

Websocketpp 使用

main.cc
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>// 1. 定义server类型
typedef websocketpp::server<websocketpp::config::asio> server_t;void onOpen(websocketpp::connection_hdl hdl)
{std::cout << "websocket长连接建立成功!\n";
}
void onClose(websocketpp::connection_hdl hdl)
{std::cout << "websocket长连接关闭!\n";
}
void onMessage(server_t *server, websocketpp::connection_hdl hdl, server_t::message_ptr msg)
{// 1. 获取有效载荷std::string body = msg->get_payload();std::cout << "收到消息:" << body << std::endl;// 2. 对客户端进行响应auto conn = server->get_con_from_hdl(hdl);conn->send(body + "-Hello!", websocketpp::frame::opcode::value::text);
}
int main()
{// 2. 实例化服务器对象server_t server;// 3. 初始化日志输出,关闭日志输出server.set_access_channels(websocketpp::log::alevel::none);// 4. 初始化asio框架server.init_asio();// 5. 设置消息处理/连接握手成功/关闭连接回调函数server.set_open_handler(onOpen);server.set_close_handler(onClose);auto msg_handler=std::bind(onMessage,&server,std::placeholders::_1,std::placeholders::_2);server.set_message_handler(msg_handler);// 6. 启用地址重用server.set_reuse_addr(true);// 7. 设置监听端口server.listen(9090);// 8. 开始监听server.start_accept();// 9. 启动服务器server.run();return 0;
}

makefile

main:main.ccg++ -o $@ $^ -std=c++17 -lpthread -lboost_system

test.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Test Websocket</title>
</head><body><input type="text" id="message"><button id="submit">提交</button><script>// 创建 websocket 实例// ws://192.168.22.129:9090// 类比 http// ws 表示 websocket 协议let websocket = new WebSocket("ws://192.168.22.129:9090");// 处理连接打开的回调函数websocket.onopen = function () {console.log("连接建立");}// 处理收到消息的回调函数// 控制台打印消息websocket.onmessage = function (e) {console.log("收到消息: " + e.data);}// 处理连接异常的回调函数websocket.onerror = function () {console.log("连接异常");}// 处理连接关闭的回调函数websocket.onclose = function () {console.log("连接关闭");}// 实现点击按钮后, 通过 websocket 实例 向服务器发送请求let input = document.querySelector('#message');let button = document.querySelector('#submit');button.onclick = function () {console.log("发送消息: " + input.value);websocket.send(input.value);} </script>
</body></html>

相关文章:

  • wordpress主题分享
  • Datawhale AI春训营 day
  • 每日算法刷题Day4 5.12:leetcode数组4道题,用时1h
  • IDEA 插件推荐:提升编程效率
  • LeetCode 2094.找出 3 位偶数:遍历3位偶数
  • 101alpha---第10
  • 16.three官方示例+编辑器+AI快速学习webgl_buffergeometry_lines_indexed
  • 嵌入式软件--stm32 DAY 6 USART串口通讯(下)
  • js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
  • 「光域」系列激光测距传感器:以光为尺,重构空间认知边界
  • Python 处理图像并生成 JSONL 元数据文件 - 固定text版本
  • Oracle19c中的全局临时表
  • 使用vite重构vue-cli的vue3项目
  • TDengine 在金融领域的应用
  • 《Head First 设计模式》第一章 - 笔记
  • 【ASR学习笔记】:语音识别领域基本术语
  • web 自动化之 Unittest 四大组件
  • 一个.Net开发的、用于自动化测试Windows应用程序的开源框架
  • Wpf学习片段
  • 从海洋生物找灵感:造个机器人RoboPteropod,它能在水下干啥?
  • 高适配算力、行业大模型与智能体平台重塑工业城市
  • 书法需从字外看,书法家、学者吴本清辞世
  • 未来之城湖州,正在书写怎样的城市未来
  • 轿车追尾半挂车致3死1伤,事故调查报告:司机过分依赖巡航系统
  • 三大交易所多举措支持科创债再扩容,约160亿证券公司科创债有望近期落地
  • 网民反映“潜水时遭遇服务质量不佳”,三亚开展核查调查