基于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);elseconn.send_text(data);});app.loglevel(LogLevel::Debug).port(18080).multithreaded().run();

3.3 运行测试

在这里插入图片描述

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot复习:(23)ImportSelector的用法

功能&#xff1a; 定一一个字符串数组&#xff0c;每个元素都是一个类的全限定名&#xff08;包名类名&#xff09;&#xff0c;把这些类的实例注册到Spring 容器。 一、定义要注册的类&#xff1a; package cn.edu.tju.service;import org.springframework.context.annotatio…

Qt自定义对话框

介绍 自定义框主要通过对现有对话框QDialog类的派生&#xff0c;根据需求编写成员函数、重载信号函数、槽函数&#xff0c;进而实现在主QWidget中点击某个按钮后&#xff0c;一个对话框的弹出 流程 简化创建派生类 最后点击完成即可。 自定义ui界面&#xff0c;编写成员函数…

产品体系架构202308版

1.前言 当我们不断向前奔跑时&#xff0c;需要回头压实走过的路。不断扩张的同时把相应的内容沉淀下来&#xff0c;为后续的发展铺垫基石。 不知从何时起&#xff0c;产品的架构就面向了微服务/中台化/前后端分离/低代码化/分布式/智能化/运行可观测化的综合体&#xff0c;让…

Git与Github常用方法

目录 1. Github基本使用方法2. Git使用方法3. git、VS code、Github联合使用方法4. Git配置Github远程仓库SSH密钥5. 常见问题 1. Github基本使用方法 仓库&#xff08;Repository&#xff09;&#xff1a;Github上用来存放代码的空间&#xff0c;包含代码、文档和其他文件。提…

逆向破解学习-经典贪吃蛇大作战_1.00

试玩游戏 有支付取消关键字 找静态代码 找到关键字 ![在这里插入图片描述](https://img-blog.csdnimg.cn/5d9f3cc57d18477794197ea5fae8c5ba.png Hook代码 import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.r…

Java 本地缓存之王:Caffeine 保姆级教程

一、Caffeine介绍 1、缓存介绍 缓存(Cache)在代码世界中无处不在。从底层的CPU多级缓存&#xff0c;到客户端的页面缓存&#xff0c;处处都存在着缓存的身影。缓存从本质上来说&#xff0c;是一种空间换时间的手段&#xff0c;通过对数据进行一定的空间安排&#xff0c;使得下…

vxe-grid\table 自定义动态列排序设置

实现动态加载的表格数据&#xff0c;根据设置动态控制指定的某些字段排序功能&#xff1b;如下图所示&#xff1b; 代码实现&#xff1a;标签内添加属性&#xff1b; :sort-config"{trigger:cell, defaultSort: {field: , order: desc}, orders:[desc, asc]}" sort-…

nginx使用心得

nginx入门 四个用途&#xff1a; 正向代理&#xff1a;内网用户访问internet 反向代理&#xff1a;对internet用户屏蔽内网服务器&#xff0c;往往与负载均衡连用。 负载均衡 web server 启停 启动nginx&#xff08;指定配置文件&#xff09; sudo nginx -c /export/home/c…

OneFlow 中的 Softmax

Softmax 是深度学习模型中的常见算子。PyTorch 的 Softmax 算子直接调用 cuDNN 的接口。而 OneFlow 内部针对输入数据的类别数量&#xff0c;采用3个 kernel 来分别处理&#xff0c;在多数情况下都可以获得比 cuDNN 更优的性能表现。下面对其实现进行介绍。OneFlow 的静态分层结…

快速修复应用程序中的问题的利器—— Android热修复

热修复技术在Android开发中扮演着重要的角色&#xff0c;它可以帮助开发者在不需要重新发布应用程序的情况下修复已经上线的应用程序中的bug或者添加新的功能。 一、热修复是什么&#xff1f; 热修复&#xff08;HotFix&#xff09;是一种在运行时修复应用程序中的问题的技术…

-bash: fork: retry: Resource temporarily unavailable 问题解决

错误提示&#xff1a; -bash: fork: retry: Resource temporarily unavailable 错误分析&#xff1a;之前已经出现过这种资源限制的报错提醒&#xff0c;然后整个系统可用的连接数就已经用完了&#xff0c;无法使用工具来获取系统信息&#xff0c;所以将运行的任务脚本kill后开…

研发工程师玩转Kubernetes——初始化容器和普通容器的区别

在现实场景中&#xff0c;我们为了降低业务耦合&#xff0c;往往会将一个大大的功能拆解成若干独立的小功能。比如主要业务启动前&#xff0c;需要将其所依赖的各种资源都拉下来。一种做法是在一个Pod内完成上述两步操作&#xff0c;但是会导致业务逻辑不够独立&#xff1b;另外…

【剑指Offer 58】翻转单词顺序,Java解密。

LeetCode 剑指Offer 75道练习题 文章目录 剑指Offer:翻转单词顺序示例:限制:解题思路:剑指Offer:翻转单词顺序 【题目描述】 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a stu…

JS 原型与继承2

//***-、原型、原型链、构造函数 prototype、 proto_、constructor function Foo(){this.a1} var foo new Foo(); Object.getPrototypeOf(foo);//访问对象原型 效果等同于&#xff0c;foo. proto &#xff0c;只是更推荐使用 Es6的 Object.getPrototypeof()方式 // construct…

【AI】《动手学-深度学习-PyTorch版》笔记(十五):网络中的层、块和参数

AI学习目录汇总 1、什么是块? 在线性模型中,我们关注过单个神经元(单个神经网络的输入和输出); 在多层感知机中,我们关注过整层的神经元(前一层的输出作为后一层的输入); 如果将“多层感知机”视为一整体,称为“块”,可以将前一个块的输出作为后一个块的输入。 块…

yum安装tcpkill时出现No package dsniff available的解决方案

今天安装tcpkill的时候提示 [jiankunkingcmp24 ~]# yum -y install dsniff Loaded plugins: fastestmirror, langpacks Repository base is listed more than once in the configuration Repository updates is listed more than once in the configuration Repository extras…

PHP序列化,反序列化

一.什么是序列化和反序列化 php类与对象 类是定义一系列属性和操作的模板&#xff0c;而对象&#xff0c;就是把属性进行实例化&#xff0c;完事交给类里面的方法&#xff0c;进行处理。 <?php class people{//定义类属性&#xff08;类似变量&#xff09;,public 代表可…

MySQL高级-存储引擎+存储过程+索引(详解01)

目录 1.mysql体系结构 2.存储引擎 2.1.存储引擎概述 2.2.1.InnoDB 2.2.2.MyISAM 2.2.3.存储引擎选择 3.存储过程 3.1.存储过程和函数概述 3.2.创建存储过程 3.3.调用存储过程 3.4.查看存储过程 3.5.删除存储过程 3.6.语法 3.6.1.变量 3.6.2.if条件判断 3.6.3.…

知网期刊《中阿科技论坛》简介及投稿须知

知网期刊《中阿科技论坛》简介及投稿须知 主管单位&#xff1a;宁夏回族自治区科学技术厅 主办单位&#xff1a;宁夏回族自治区对外科技交流中心(中国一阿拉伯国家技术转移中心) 刊  期&#xff1a;月刊 国际刊号&#xff1a;ISSN 2096-7268 国内刊号&#xff1a;CN 64-…