基于Crow的C++的WebSocket服务器

基于Crow的C++的WebSocket服务器

一、WebSocket

1.1 什么是WebSocket

WebSocket 是一种持久化的通讯协议。
很多网站为了实现推送技术,所用的技术都是轮询,这种解决方案是指由浏览器每隔一段时间向服务器发出 HTTP 请求,然后服务器返回最新的数据给客户端。这种模式有很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。

WebSocket 是一种网络传输协议,其本质是前端与服务器端建立一条TCP长连接,服务端可以随时向前端推送数据,前端也可以随时向服务端发送数据,实现了两者间双向数据实时传输。WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455,后由 RFC 7936 补充规范。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

WebSocket 与 HTTP 对比如下:
在这里插入图片描述

WebSocket 的优点:

  • 1)较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小;
  • 2)更强的实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少;
  • 3)保持连接状态:与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息;
  • 4)更好的二进制支持:WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容;
  • 5)可以支持扩展:WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。

1.2 协议结构

1.2.1 数据帧结构

前面提到的RFC6455中定义了数据帧的格式,如下:

在这里插入图片描述

数据帧的组成结构和其他协议类似,都是数据头+消息体(payload,直译为荷载),不过WebSocket的数据头长度是可变的,受两个因素影响:

  • 消息体长度的大小,也就是图上的Payload len: 占7或7+16或7+64bit。
  • 是否有maks key,有的话头部多4个字节。

1.2.1 数据帧格式

  • FIN: 1bit

    • 0 表示不是消息的最后一个分片
    • 1 表示是消息的最后一个分片
  • RSV1, RSV2, RSV3: 各占1bit, 一般情况下全为0, 与Websocket拓展有关, 如果出现非零的值且没有采用WebSocket拓展, 连接出错。

  • Opcode: 4bit

    • %x0: 表示本次数据传输采用了数据分片, 当前数据帧为其中一个数据分片
    • %x1: 表示这是一个文本帧
    • %x2: 表示这是一个二进制帧
    • %x3-7: 保留的操作代码, 用于后续定义的非控制帧
    • %x8: 表示连接断开
    • %x9: 表示这是一个心跳请求(ping)
    • %xA: 表示这是一个心跳响应(pong)
    • %xB-F: 保留的操作代码, 用于后续定义的非控制帧
  • Mask: 占1bit

    • 0表示不对数据载荷进行掩码异或操作
    • 1表示对数据载荷进行掩码异或操作
  • Payload len: 占7或7+16或7+64bit

    • 0~125: 数据长度等于该值
    • 126: 后续的2个字节代表一个16位的无符号整数, 值为数据的长度
    • 127: 后续的8个字节代表一个64位的无符号整数, 值为数据的长度
  • Masking-key: 占0或4bytes;只有客户端给服务器端发送数据时才会有masking key,服务器端给客户端发送数据不需要masking key

    • 1: 携带了4字节的Masking-key
    • 0: 没有Masking-key
  • payload data: 消息体

1.2.2 掩码算法

结构体中有一个Masking-key,这是一个掩码字段,它是一个由客户端随机选择的 32 位的值。掩码值必须是不可被预测的。因此,掩码必须来自强大的熵源(entropy),并且给定的掩码不能很容易的预测到后续帧,掩码的不可预测性对于预防恶意应用的作者在网上暴露相关的字节数据至关重要。

掩码只影响头的长度,不影响消息体,对数据进行掩码操作和对数据进行反掩码操作所涉及的步骤是相同的。

掩码、反掩码操作都采用如下算法:

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j

其中:

  • 1)original-octet-i:为原始数据的第 i 字节;
  • 2)transformed-octet-i:为转换后的数据的第 i 字节;
  • 3)masking-key-octet-j:为 mask key 第 j 字节。

算法是完全公开的,运算起来也不复杂。所以数据掩码的作用是增强协议的安全性。而不是为了保护数据本身,应该主要还是为了防止早期版本的协议中存在的代理缓存污染攻击等问题。

1.3 与WebSocket类似的协议

1.3.1 AJAX Polling:

客户端使用普通的http方式向服务器端请求网页,同时执行轮询脚本,定期循环的向服务器发送请求(例如每5秒发送一次请求),获取信息
服务器对每次请求作出响应,并返回相应信息,就像正常的http请求一样;
缺点:轮询耗费资源

1.3.2 AJAX Long-Polling:

与普通的polling相比区别就是异步等待;
客户端使用普通的http方式向服务器端请求网页,同时执行轮询脚本,向服务器发送数据、请求信息;
但是服务器并不是立即就对客户端的请求作出响应,而是等待有效的更新;
当信息是有效的更新时,服务器才会把数据推送给客户端;
当客户端接收到服务器的通知时,立即会发送一个新的请求,进入到下一次的轮询;
缺点:无更新时发送请求浪费资源

1.3.3 Server Sent Events (SSE) / EventSource:

客户端使用普通的http方式向服务器端请求网页,同时网页与服务器之间建立了一个连接;
当服务器端有更新时,会发送一个事件到客户端;
服务器无更新但客户端有请求时,仍使用HTTP模式进行请求与应答;
优点是如果已有的代码是HTTP请求,那么服务器可以不影响现有代码的情况下支持SSE;
缺点是半双工,客户端无法向服务器发送请求,并且部分浏览器不支持。

1.3.4 Comet:

Comet是一种用于web的推送技术,能使服务器实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式,长轮询和iframe流。

  • 长轮询
    长轮询是在打开一条连接以后保持,等待服务器推送来数据再关闭的方式。
  • iframe流
    iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长链接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。
    iframe流方式的优点是浏览器兼容好,Google公司在一些产品中使用了iframe流,如Google Talk。

1.3.5 WebRTC

WebRTC 是一种实时媒体传输技术,最初是一个开源项目,主要目标是为浏览器和移动应用程序提供建立连接的方法。一个连接是通过指示信号和同步信号建立的,这个过程被称为信令。为了在两个设备之间建立 WebRTC 连接,需要一个信令服务器。它是一个中间件,除了承担建立连接的主要功能外,还最大程度上降低了有价值的信息和机密数据泄露的风险。
WebRTC的延迟很低,很适合用于大数据量的传输;并且因为支持自定义协议,所以安全性可控;缺点是只有部分浏览器支持,以及需要中间件等。

二、CroW

2.1 什么是Crow

Crow是一个用C++编写的WebSocket框架,旨在提供简单易用的API和高性能。它最初是由Mozilla开发的,现在已经成为了一个独立的开源项目。它使用了类似于Python的Flask的路由,这使得它易于使用,同时其处理速度也很快。

2.2 优点以及缺点

2.2.1 优点

简单易用:Crow提供了简单易用的API,可以快速构建WebSocket服务器和客户端应用程序。
高性能:Crow使用了Asio库来处理网络I/O操作(旧版使用Boost.asio,目前已更新为独立版asio),可以实现高效的并发处理。此外,它还支持SSL/TLS加密,可以保证通信的安全性和可靠性。
可扩展性:Crow提供了插件机制,可以方便地扩展其功能。例如,可以通过编写自定义插件来实现自定义的消息处理逻辑。
跨平台:Crow可以在多种平台上运行,包括Windows、Linux和macOS等操作系统。

2.2.2 缺点

文档不够完善:Crow的文档很清晰简洁,相比于其他一些WebSocket框架来说,它的文档还有待进一步完善。
缺乏实时消息队列支持:Crow目前还不支持实时消息队列功能,如果需要实现类似消息队列的功能,需要自己编写相关的代码。
不支持多路复用:Crow目前只支持单个TCP连接,如果需要同时处理多个客户端请求,需要为每个客户端创建一个新的线程或进程。
不支持HTTPS:虽然Crow支持SSL/TLS加密,但是不支持HTTPS协议。如果需要通过HTTPS协议进行通信,需要自己实现相关的加密算法。
不适合大规模应用:由于Crow的设计初衷是用于小型到中型的应用场景,因此在面对大规模应用时可能会出现性能瓶颈等问题。

2.3 编译安装

在安装Crow之前需要先安装ASIO,要求1.22.0以上的版本;

然后下载、MAKE、install:

git clone https://github.com/CrowCpp/Crow.git
mkdir build
cd build
cmake .. -DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF
make install

之后能看到如下目录:
在这里插入图片描述
其中include里的就是所有的源码,平时用的时候只需要包含include/crow.h即可

三、基于Crow的C++服务器与客户端示例

3.1 客户端

客户端我是用HTML+JS那一套,Crow也可以写C++客户端;
JS里面使用WebSocket可以直接使用WebSocket对象,这个类只有两个函数

var wSocket = new WebSocket("ws://localhost:18080/ws");
wSocket.close([code[, reason]]) // 关闭当前链接
wSocket.send(data) // 发送数据

写一个html,在里面加一个按钮,点击然后调用send发送数据即可;
具体可参考: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

3.2 服务器

先定义Crow::app对象,然后绑定ws路由,再监听指定端口即可,

    CROW_WEBSOCKET_ROUTE(app, "/ws")
        .onopen([&](crow::websocket::connection& conn) {
                CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip();
            })

        .onclose([&](crow::websocket::connection& conn, const std::string& reason) {
                CROW_LOG_INFO << "websocket connection closed: " << reason;
            })

        .onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary) 
            {
                CROW_LOG_INFO << "Recv message:" << data;
                if (is_binary)
                    conn.send_binary(data);
                else
                    conn.send_text(data);
            });

    app.loglevel(LogLevel::Debug).port(18080).multithreaded().run();

3.3 运行测试

在这里插入图片描述

还可以使用测试网站:http://www.websocket-test.com/ (这个网站是我看到某个文章上推的,安全性未知)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/67047.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React源码解析18(2)------ FilberNode,FilberRootNode结构关系

摘要 在上一篇&#xff0c;我们实现了通过JSX转换为ReactElement的方法&#xff0c;也看到了转换后React元素的结构。但是这个React元素&#xff0c;并不能很清楚的表达组件之间的关系&#xff0c;以及属性的处理。 所以在React内部&#xff0c;会将所有的React元素转换为Fil…

idea集成svn

一、注意 安装svn客户端的时候一定要勾选&#xff0c;否则在idea上集成svn的时候会找不到 svn.exe 而报错。 如果当初安装时忘记勾选&#xff0c;重新运行安装包&#xff0c;选择modify&#xff0c;勾选command line client tools项中的内容。 二、配置idea集成svn 三、检出(c…

电影票接单售票小程序搭建--java开源+后台管理系统

要搭建一个电影票接单售票小程序和后台管理系统&#xff0c;可以采取以下步骤&#xff08;以下步骤不分先后&#xff09;&#xff1a; 设计系统架构首先需要设计系统的整体架构&#xff0c;确定系统的技术选型、功能模块和业务流程等。可以考虑使用微服务架构&#xff0c;将系…

Jmeter添加cookie的两种方式

jmeter中添加cookie可以通过配置HTTP Cookie Manager&#xff0c;也可以通过HTTP Header Manager&#xff0c;因为cookie是放在头文件里发送的。 实例&#xff1a;博客园点击添加新随笔 https://i.cnblogs.com/EditPosts.aspx?opt1 如果未登录&#xff0c;跳转登录页&#xf…

MongoDB SQL

Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。C:\Users\Administrator>cd C:\MongoDB\Server\3.4\binC:\MongoDB\Server\3.4\bin> C:\MongoDB\Server\3.4\bin> C:\MongoDB\Server\3.4\bin>net start MongoDB 请求的…

ArcGIS、ENVI、InVEST、FRAGSTATS技术教程

专题一 空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 专题二 ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与注…

FPGA应用学习笔记--时钟域的控制 亚稳态的解决

时钟域就是同一个时钟的区域&#xff0c;体现在laways语句边缘触发语句中&#xff0c;设计规模增大就会导致时钟不同步&#xff0c;有时差&#xff0c;就要设计多时钟域。 会经过与门的延时产生的新时钟域&#xff0c;这种其实不推荐使用&#xff0c;但在ascl里面很常见 在处理…

开发工具Eclipse的使用

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Eclipse使用的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Eclipse是什么 二.使用Eclipse的…

openGauss学习笔记-35 openGauss 高级数据管理-ALTER TABLE语句

文章目录 openGauss学习笔记-35 openGauss 高级数据管理-ALTER TABLE语句35.1 语法格式35.2 参数说明35.3 示例 openGauss学习笔记-35 openGauss 高级数据管理-ALTER TABLE语句 修改表&#xff0c;包括修改表的定义、重命名表、重命名表中指定的列、重命名表的约束、设置表的所…

激活函数总结(四):Hard系列激活函数补充(HardSigmoid、HardTanh、Hardswish)

激活函数总结&#xff08;四&#xff09;&#xff1a;Hard系列激活函数补充 1 引言2 激活函数2.1 HardSigmoid激活函数2.2 HardTanh激活函数2.3 Hardswish激活函数 3. 总结 1 引言 在前面的文章中已经介绍了过去大家较为常见的激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PRe…

Vue中使用qrcode说明

1.安装 npm i qrcode1.5.3 2.导入 import QRCode from qrcode 3.转换 说明&#xff1a;拿到服务器传来的字符串&#xff0c;转换成base64&#xff0c;然后通过img标签展示。 // 字符串转成二维码 let result await this.$API.reqPayInfo(this.orderId); 总结&#xff1a;

资深媒体人宋繁银加入《数据猿》任总编辑,全面负责公司整体内容工作

大数据产业创新服务媒体 ——聚焦数据 改变商业 2023年7月北京&#xff0c;《数据猿》宣布正式任命宋繁银为总编辑&#xff0c;全面负责公司整体内容工作。此次重要的人事任命标志着《数据猿》的发展迈上了一个新的台阶&#xff0c;对于《数据猿》团队而言&#xff0c;不仅是一…

客户端渲染和服务端渲染的区别

一、客户端渲染 页面的渲染工作都是由浏览器来完成的&#xff0c;服务器只是负责提供数据。前端去后端取数据生成DOM树。 **最少两次请求 ** 第一次请求拿到的是页面第二次请求拿到的是动态数据 二、服务端渲染 页面渲染的工作都是由服务端来完成的&#xff0c;数据也是由服…

python版《羊了个羊》游戏开发第一天

Python小型项目实战教学课《羊了个羊》 一、项目开发大纲&#xff08;初级&#xff09; 版本1.0&#xff1a;基本开发 课次 内容 技术 第一天 基本游戏地图数据 面向过程 第二天 鼠标点击和移动 面向对象 第三天 消除 设计模式&#xff1a;单例模式 第四天 完整…

OpenCV 中的光流 (C++/Python)

什么是光流? 光流是一项视频中两个连续帧之间每像素运动估计的任务。基本上,光流任务意味着计算像素的位移矢量作为两个相邻图像之间的对象位移差。光流的主要思想是估计物体由其运动或相机运动引起的位移矢量。 理论基础 假设我们有一个灰度图像——具有像素强度的矩阵。我…

JavaSE【继承和多态】(1)(重点:初始化、pretected封装、组合)

一、继承 继承 (inheritance) 机制 &#xff1a;是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特 性 的基础上进行扩展&#xff0c;增加新功能 &#xff0c;这样产生新的类&#xff0c;称 派生类 。 继承呈现了面向对象程序设计的层次结…

在vue项目使用数据可视化 echarts ,柱状图、折线图、饼状图使用示例详解及属性详解

官网地址&#xff1a;Apache ECharts ​一、下载插件并在页面中引入 npm install echarts --save 页面导入&#xff1a; import * as echarts from echarts 全局导入&#xff1a; main.js 中&#xff0c;导入并注册到全局 import echarts from echarts Vue.prototype.$echart…

Flume原理剖析

一、介绍 Flume是一个高可用、高可靠&#xff0c;分布式的海量日志采集、聚合和传输的系统。Flume支持在日志系统中定制各类数据发送方&#xff0c;用于收集数据&#xff1b;同时&#xff0c;Flume提供对数据进行简单处理&#xff0c;并写到各种数据接受方&#xff08;可定制&…

日常开发中Git命令指北

Git基本操作 创建化仓库 mkdir 目录 cd 目录 git init配置本地仓库 # 配置用户名&#xff0c;邮箱 git config user.name "cxf" git config user.email "1969612859qq.com" # 查看本地配置&#xff08;小写的 L&#xff09; git config -l # 重置配置&a…

【SpringCloud】RabbitMQ基础

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;…