那些很重要,但是不常用的技术,websocket
connygpt 2024-09-27 02:25 7 浏览
目录
1. 为什么会有websocket
2. websocket协议格式
3. 协议具体实现
一、为什么需要 WebSocket?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者HTTP连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket就是这样发明的
WebSocket协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
websocket协议格式
Browser已经支持http协议,为什么还要开发一种新的WebSocket协议呢?我们知道http协议是一种单向的网络协议,在建立连接后,它只允许Browser/UA(UserAgent)向WebServer发出请求资源后,WebServer才能返回相应的数据。而WebServer不能主动的推送数据给Browser/UA,当初这么设计http协议也是有原因的,假设WebServer能主动的推送数据给Browser/UA,那Browser/UA就太容易受到攻击,一些广告商也会主动的把一些广告信息在不经意间强行的传输给客户端,这不能不说是一个灾难。那么单向的http协议给现在的网站或Web应用程序开发带来了哪些问题呢?
- 一条连接上只可以发送一个请求
- 请求只能从客户端开始。客户端不可以接收除了响应以外的指令。
- 请求 / 响应首部未经过压缩就直接进行传输。首部的信息越多,那么延迟就越大。
- 发送冗长的首部。每次互相发送相同的首部造成的浪费越多
- 可以任意选择数据压缩格式。非强制压缩发送
ajax轮询
ajax(异步的javascript与xml技术)是一种有效利用javascript和dom的操作,以达到局部web页面的提花和加载的异步通信手段。和以前的同步通信相比,他只更新一部分页面,相应中传输饿数据量会因此的减少。
ajax轮询的原理是,让浏览器每隔一段时间就发送一次请求,询问服务器是否有新消息。
而利用ajax实时的从服务器获取内容,有可能导致大量的请求产生。
长轮询
原理和ajax轮询差不多,都是采用轮询的方式,不过采用的是阻塞模型。也就是说,当客户端发起连接后,如果服务器端内容没有更新,将响应至于挂起状态,一直不回复response给客户端,知道有内容更新,再返回响应。
虽然可以做到实时更新,但是为了保留响应,一次连接饿持续时间也变长了。期间,为了维持连接会消费更多的资源。
从上面两种方式中,其实可以看出是再不断的建立http连接,然后等待服务器处理,可以体现出了http的特点:被动性,即:请求只能由客户端发起。服务器端不能主动联系客户端。
不管怎么样,上面这两种都是非常消耗资源的。
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
长轮询 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
除了以上这些,HTTP还是一个无状态协议。
通俗的说就是,服务器因为每天要接待太多浏览器了,是个健忘鬼,你一断连接,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。
WebSocket
WebSocket其实是HTTP协议上的一种补充,他们有交集但并不是全部。
一旦web服务器和客户端建立起websocket协议的通信连接,之后所有的通信都依靠这个专用连接进行。只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。
websocket是基于HTTP协议的,或者说借用了http的协议来完成一部分握手。为了实现websocket通信,在http建立连接后,还需要进行一次“握手”的步骤。
握手 · 请求
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
为了实现websocket通信,需要用到http的Upgrade首部字段,告知服务器通信协议已发生改变:我要发起的是websocket协议。以达到握手的目的。
Sec-WebSocket-Key字段记录着握手必不可少的键值,用于验证服务器是否支持websocket通信。
Sec-WebSocket-Protocol字段记录的是所需要使用的协议。
握手 · 响应
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
对于客户端的请求,服务器返回状态码 101 Switching Protocols的响应。
返回Upgrate告诉客户端即将升级的协议是Websocket协议。
Sec-WebSocket-Accept字段值是由握手请求中的Sec-WebSocket-Key字段值加密过后生成的。
Sec-WebSocket-Protocol 则是表明最总使用的协议。
到这里,http已经完成所有他的工作了,接下来通信时不再使用HTTP的数据帧,而是使用websocket独立的数据帧。
因此,websocket协议具有以下的特点:
- 推送功能
支持服务器端向客户端推送功能。服务器可以直接发送数据而不用等待客户端的请求。 - 减少通信量
只要建立起websocket连接,就一直保持连接,在此期间可以源源不断的传送消息,直到关闭请求。也就避免了HTTP的非状态性。 - 减少资源消耗
那么为什么他会解决服务器上消耗资源的问题呢?
其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。
简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)。本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢。导致客服不够。Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员在统一转交给客户。这样就可以解决客服处理速度过慢的问题了。
协议具体实现
前提:
本人最近做的项目,服务器端用的是C++写的,而与客户端交互用的是websocket,服务器端要想正常的使用数据,必须要对websocket协议进行解析。
WebSocket数据格式
- FIN:表示这个数据是不是接收完毕,为1表示收到的数据是完整的,占1bit
- RSV1~3:用于扩展,通常都为0,各占1bit
- OPCODE:表示报文的类型,占4bit 0x00:标识一个中间数据包0x01:标识一个text数据包0x02:标识一个二进制数据包0x03~07:保留0x08:标识一个断开连接数据包0x09:标识一个ping数据包0x0A:标识一个pong数据包0x0B~F:保留
- MASK:用于表示数据是否经常掩码处理,为1时,Masking-key即存在,占1bit
- Payload len:表示数据长度,即Payload Data的长度,当Payload len为0~125时,表示的值就是Payload Data的真实长度;当Payload len为126时,报文其后的2个字节形成的16bits无符号整型数的值是Payload Data的真实长度(网络字节序,需转换);当Payload len为127时,报文其后的8个字节形成的64bits无符号整型数的值是Payload Data的真实长度(网络字节序,需转换);
- Masking-key:掩码,当Mask为1时存在,占4字节32bit
- Payload Data:表示数据
C++对websocket协议处理
/**
* @brief getWSFrameData 解析websocket的协议包,不能解决粘包半包问题
* @param msg 待解析的数据
* @param msgLen 待解析的数据长度
* @param outBuf 解析完成数据
* @return
*/
int unPackingWSFrameData(char *msg,
int msgLen,
std::vector<char> &outBuf)
{
//报文长度一定大于2字节,对于小于的,做返回处理
if(msgLen < 2)
{
return -3;
}
uint8_t opcode_ = 0;
uint8_t mask_ = 0;
uint8_t masking_key_[4] = {0,0,0,0};
uint64_t payload_length_ = 0;
int pos = 0;
//Opcode
opcode_ = msg[pos] & 0x0f;
pos++;
//MASK
mask_ = (unsigned char)msg[pos] >> 7;
//Payload length
payload_length_ = msg[pos] & 0x7f;
pos++;
if(payload_length_ == 126)
{
uint16_t length = 0;
memcpy(&length, msg + pos, 2);
pos += 2;
payload_length_ = ntohs(length);
}
else if(payload_length_ == 127)
{
uint32_t length = 0;
memcpy(&length, msg + pos, 8);
pos += 8;
payload_length_ = ntohl(length);
}
//Masking-key
if(mask_ == 1)
{
for(int i = 0; i < 4; i++)
{
masking_key_[i] = msg[pos + i];
}
pos += 4;
}
//取出消息数据
if (msgLen >= pos + payload_length_ )
{
outBuf.clear();
if(mask_ != 1)
{
char* dataBegin = msg + pos;
outBuf.insert(outBuf.begin(), dataBegin, dataBegin+payload_length_);
}
else
{
for(uint i = 0; i < payload_length_; i++)
{
int j = i % 4;
outBuf.push_back(msg[pos + i] ^ masking_key_[j]);
}
}
}
else
{
//此时包长小于报文中记录的包长
return -2;
}
//断开连接类型数据包
if ((int)opcode_ == 0x8)
return -1;
return 0;
}
以上函数即实现了对收到websocket数据的解析,返回结果为:vector<char>output;
通常会在函数外面对此进行转换为char*,方便我们使用,见下:
vector<char>output;
char* out = &output[0];
当然,现在的解析还不是完美的解决方法,因为在实际的使用当中,会存在接收的包粘包,半包等等问题,而以上函数只能解决收到包正好是一个完整的包的情况;具体解决粘包半包问题,留待下次博客吧!
参考资料:后台私信“资料”送
结尾:
只为记录,只为分享! 愿所写能对你有所帮助。不忘记点个赞,谢谢~
后台私信可以领取 内容包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体, WebRTC ,CDN,P2P,K8S,Docker,Golang, TCP/IP,MTK , 嵌入式 , 协程,DPDK等等 。
相关推荐
- 3分钟让你的项目支持AI问答模块,完全开源!
-
hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...
- 干货|程序员的副业挂,12个平台分享
-
1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...
- Github标星超200K,这10个可视化面板你知道几个
-
在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...
- 开箱即用的炫酷中后台前端开源框架第二篇
-
#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...
- 搭建React+AntDeign的开发环境和框架
-
搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...
- 基于.NET 5实现的开源通用权限管理平台
-
??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...
- StreamPark - 大数据流计算引擎
-
使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...
- 教你使用UmiJS框架开发React
-
1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...
- 简单在线流程图工具在用例设计中的运用
-
敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...
- 【开源分享】神奇的大数据实时平台框架,让Flink&Spark开发更简单
-
这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...
- 聊聊规则引擎的调研及实现全过程
-
摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...
- 【开源推荐】Diboot 2.0.5 发布,自动化开发助理
-
一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...
- 微软推出Copilot Actions,使用人工智能自动执行重复性任务
-
IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...
- Electron 使用Selenium和WebDriver
-
本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...
- Quick 'n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具
-
一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...
- 一周热门
- 最近发表
- 标签列表
-
- kubectlsetimage (56)
- mysqlinsertoverwrite (53)
- addcolumn (54)
- helmpackage (54)
- varchar最长多少 (61)
- 类型断言 (53)
- protoc安装 (56)
- jdk20安装教程 (60)
- rpm2cpio (52)
- 控制台打印 (63)
- 401unauthorized (51)
- vuexstore (68)
- druiddatasource (60)
- 企业微信开发文档 (51)
- rendertexture (51)
- speedphp (52)
- gitcommit-am (68)
- bashecho (64)
- str_to_date函数 (58)
- yum下载包及依赖到本地 (72)
- jstree中文api文档 (59)
- mvnw文件 (58)
- rancher安装 (63)
- nginx开机自启 (53)
- .netcore教程 (53)