WebSocket协议解析

文章目录

    • 概要
    • 一、WS原理
        • 1.1、帧格式
    • 二、WS实战
        • 2.1、客户端发起协议升级请求
        • 2.2、服务端响应协议升级
        • 2.3、核心事件
        • 2.4、心跳保活
    • 三、总结

概要

项目中的IM系统是基于WebSocket做的,所以这里聊一下。
说到WS,不得不提HTTP,HTTP是基于TCP,面向文本的,无状态的,半双工通信协议。由于HTTP1.0的缺陷,后面版本做相应的优化:

  • 每次请求都要建立TCP连接,并且请求一个文档需要两倍RTT(一个是TCP握手,一个是请求和接收文档)。针对这种情况HTTP1.1引入了keep-alive机制,实现了建立一次TCP连接,可发送多次请求。
  • HTTP是明文传输,所以有一个中间人攻击的漏洞。针对这种情况引入了HTTPS(HTTP+TLS),实现了防窃听(数据加密)、防冒充(身份验证)、防篡改(完整性校验)。
  • HTTP的请求一般是非流水线模式【收到上一个请求的响应后才能发下一个请求】(注意现代浏览器并不启用流水线模式,实现复杂,问题多),这里有队头阻塞问题,所以有了HTTP2。
  • HTTP2只是解决了应用层的队头阻塞问题,但是TCP有丢包重传等机制,所以传输层也有队头阻塞问题,这个时候就要HTTP3登场了,引入 QUIC 协议,基于UDP,可以完美解决队头阻塞问题。

通过上面分析HTTP迭代的原因,那么WS的出现是为了解决HTTP什么问题呢?

针对HTTP请求-响应这种半双工通信模式,既要实现全双工通信

为什么要这样呢?主要是因为在没有WS之前,HTTP要实现IM,服务端事件推送等实时场景是很麻烦的,常见的有短轮询,长轮询,SSE(Server Send Event),针对实时通讯的场景方案都不是很优秀,所以有了WS,它最初是在 HTML5 中引入的。经过多年发展后,该协议慢慢被多个浏览器支持,RFC 在 2011 年就把该协议作为一个国际标准,叫 rfc6455。

也许是为了修复HTTP缺陷而诞生的,所以WS的建立是依赖HTTP的,同样规定默认使用80,443端口,当然其错误码是与HTTP不重合的。

一、WS原理

WebSocket 是一种基于TCP长连接,面向报文(二进制),支持全双工通信的网络协议。其与HTTP是平级的,相比HTTP1.1,优点如下:

  • 支持全双工通信,实时性更强;
  • 客户端与服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息。即发送端将消息切割成多个帧,并发送给接收端;接收端接收消息帧,并将关联的帧重新组装成完整的消息;
  • 长连接,有状态;
  • 较少的控制开销。连接创建后,WS客户端、服务端进行数据交换时,协议控制的数据包头部较小,不像HTTP每次请求都要携带那么多头;
  • 支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议(比如支持自定义压缩算法等)。

1.1、帧格式

WebSocket主要是解决HTTP无法实时通信的问题,所以没有HTTP2 的多路复用,优先级等复杂功能,所以二进制帧格式](https://www.rfc-editor.org/rfc/rfc6455.html#page-27)简单:
ws-datagram
可以看到帧头是16bit,2字节:

  • 第一个字节:
    前四位是标志位,第一个标志位是FIN,表示报文结束,因为一个消息可能被拆分成多帧,当接收方收到携带FIN标志位的帧时,就会把前面的帧拼起来组成完整的消息。后三位是保留标志位,必须设置为0,除非想要自定义扩展。
    后四位是opcode,也就是帧类型,比如0表示连续帧,1表示帧内容是文本,2表示帧内容是二进制,8表示关闭连接,9和10表示ping和pong等
  • 第二个字节:
    它的第一位是掩码标志位 MASK,表示帧内容是否使用异或操作(xor)做简单的加密,当该位被设置为 1 时表示加密,设置为 0 时表示不加密。如果加密了,那么必须解密才能得到正确内容。目前的 WebSocket 标准规定,客户端发送数据必须使用掩码加密,而服务器发送则不使用掩码加密。
    后面7位表示Payload Length,也就是有效负载或者说有效业务消息的长度,并且采用的是大端存储。但是7位大表示127,如果payload data大于127字节咋办??
    所以就引入了 Extended payload length 来表示真是的Payload Length:
    Payload Length ==127,则 后面8字节表示 Extended payload length;
    Payload Length ==126,则 后面2字节表示 Extended payload length;
    Payload Length < 126,则 没有 Extended payload length;

可知最大payload data长度 可以用8字节表示。

二、WS实战

  • WS是基于HTTP协议实现的。
  • WSS是基于HTTPS协议实现的,即完成tls握手后再进行协议升级。

WS建立复用了 HTTP 的握手请求过程。
客户端通过 HTTP 请求与 WebSocket 服务端协商升级协议。协议完成后,后续的数据交互则遵循 WebSocket 的协议。

2.1、客户端发起协议升级请求

GET /websocket HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13 #表示WS的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader头,里面包含服务端支持的版本号
Origin: http://www.jsons.cn
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: 9zQPEx8m0sqMkV1vanwJIA== #与服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接(比如特殊的HTTP请求被意外识别成WS)
Connection: keep-alive, Upgrade   #告诉服务端要升级协议了
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket             #告诉服务端要升级成WS协议

2.2、服务端响应协议升级

HTTP/1.1 101 Switching Protocols  #状态码 101 表示协议切换成功
Upgrade: websocket  #表示升级到WS协议
Connection: Upgrade  #表示协议升级
Sec-WebSocket-Accept: MzzLu4vmstovah28VVOvEgveq8o=  #根据客户端请求首部的 Sec-WebSocket-Key 计算出来
#将 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接。
#通过 SHA1 计算出摘要,并转成 base64 字符串。计算公式如下:
# Base64(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))

协议升级完成后就完全遵循WS协议发送接收数据了。

2.3、核心事件

WebSocket协议也是事件驱动的,客户应用程序不需要轮序服务来得到更新的数据,消息和事件将在服务器发送它们的时候异步到达。

  1. open 连接建立时触发
  2. message 接收/发送消息时触发
  3. error 通信发生错误时触发
  4. close 连接关闭时触发

2.4、心跳保活

这个其实在1.1小节已经说过了,就是opcode类型,通过ping和pong来实现,不需要自己再写保活接口了,只需要使用ping/pong即可。
以go gorilla/websocket为例:

func WebSocket(c *gin.Context) {//支持协议升级conn, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer,      	c.Request, nil)if err != nil {http.NotFound(c.Writer, c.Request)return}//添加保活机制conn.SetPongHandler(func(appData string) error {library.ZapDebug(appData)//check somethingreturn nil})//deal websocket connectfor {//接收消息mt, msg, err := conn.ReadMessage()if err != nil { //错误,打Trace,因为可能是主动或者网络问题library.ZapDebug("ReadMessage err:%v", err)continue}//这里可以go func(){}() 包一下解决并发问题{//业务处理library.ZapDebug("mt=%v,msg=%v", mt, string(msg))//响应消息err = conn.WriteMessage(mt, []byte(fmt.Sprintf("hello:%s", msg)))if err != nil {library.ZapDebug("WriteMessage err:%v", err)}}}
}

三、总结

经过上述内容,可以知道WS可以看做是对HTTP打的补丁,主要是为了解决HTTP半双工通信弊端,侧重处理数据实时通信场景。

这里再说下WS为什么没有替代HTTP,反而后面出了一样支持长连接的HTTP2 ?

  1. WS协议简单,可以说只是在TCP之上提供了数据拆包组包的功能。所以对于静态资源的请求效率是低下的,而不像HTTP2提供了多路复用,请求优先级等特性来提高传输效率,增强页面渲染。
  2. 长连接,全双工通信是WS相比于HTTP的一大优势,其实HTTP2也有,但奈何浏览器为了提高页面渲染效率往往还是同时开多个TCP连接。可以这样说,WS与HTTP各有优势,在页面渲染和短请求场景下选HTTP,在实时通信,如IM等场景下选WS,二者结合使用最好。
  3. 长连接也并不是银弹,随着在线用户增加,相同资源下,其最大在线用户数量理论上不如HTTP。
  4. 长连接的负载均衡不如短连接灵活,简单。

可以这样说,如果不是浏览器是个沙河环境,不允许用户使用TCP,说不定WS压根不会诞生。

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

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

相关文章

【每天40分钟,我们一起用50天刷完 (剑指Offer)】第四十二天 42/50【unordered_set】【双指针处理连续】【翻转字符串】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

AD21原理图的高级应用(五)自定义原理图模板及调用

&#xff08;五&#xff09;自定义原理图模板及调用 1.创建原理图模板2.调用原理图模板 1.创建原理图模板 利用 Altium Designer 软件在原理图中创建自己的模板,可以在图纸的右下角绘制一个表格用于显示图纸的一些参数,例如文件名、作者、修改时间、审核者、公司信息、图纸总数…

shopee,lazada,etsy店群如何高效安全的管理

对于电商卖家来说&#xff0c;要经营多个店铺&#xff0c;管理多个账号是非常常见的操作。为了避免账号关联被平台识别出来&#xff0c;需要使用防关联的浏览器来进行操作 ​1、支持多平台 支持同时管理多个电商平台店铺&#xff0c;Shopee、Lazada、etsy、poshmark、vinted等&…

Vue.js 生命周期函数

系列文章目录 Vue.js基础简答题 文章目录 系列文章目录前言一、创建阶段1.beforeCreate2.created3.beforeMount4.mounted 二、运行阶段1.beforeUpdate2.updated 三、销毁阶段1.beforeDestroy2.destroyed 总结 前言 Vue.js 生命周期指的是Vue实例的生命周期&#xff1b; Vue实…

【RabbitMQ】Linux系统服务器安装RabbitMQ

一、下载 首先应该下载erlang&#xff0c;rabbitmq运行需要有erland环境。 官网地址&#xff1a;https://www.erlang.org/downloads 下载rabbitmq 官网环境&#xff1a;https://www.rabbitmq.com/download.html 注意&#xff1a;el7对应centos7&#xff0c;el8对应centos8…

在Word中快速输入方框对号

在Word中输入方框对号播报文章 先输入“2611”&#xff0c;然后同时按ALTX&#xff0c; 插入 符号 其他符号

主流开源监控系统一览

减少故障有两个层面的意思&#xff0c;一个是做好常态预防&#xff0c;不让故障发生&#xff1b;另一个是如果故障发生&#xff0c;要能尽快止损&#xff0c;减少故障时长。而监控的典型作用&#xff0c;就是帮助我们发现及定位故障&#xff0c;这两个环节对于减少故障时长至关…

STM32 串口学习(二)

要用跳线帽将PA9与RXD相连&#xff0c;PA10与TXD相连。 软件设计 void uart_init(u32 baud) {//UART 初始化设置UART1_Handler.InstanceUSART1; //USART1UART1_Handler.Init.BaudRatebound; //波特率UART1_Handler.Init.WordLengthUART_WORDLENGTH_8B; //字长为 8 位数据格式U…

CAN通信的位定时与同步

位定时与同步 1.位时间 1.1相关基本概念 1&#xff09;系统时钟&#xff1a;记为 t c l k t_{clk} tclk​&#xff1b; 2&#xff09;CAN时钟周期&#xff1a;CAN时钟是由系统时钟分频而来的一个时间长度值&#xff0c;表示CAN控制器的工作时钟&#xff0c;实际上就是一个时…

网络编程套接字

网络编程套接字 预备知识理解源IP地址和目的IP地址认识端口号理解 "端口号" 和 "进程ID"理解源端口号和目的端口号认识TCP协议认识UDP协议网络字节序 socket编程接口socket 常见APIsockaddr结构 简单的UDP网络程序UDP通用服务端udp服务端初始化udp服务端启…

自动化运维工具——Ansible

自动化运维工具——Ansible 一、Ansible概述二、ansible 环境安装部署1.管理端安装 ansible2.ansible 目录结构3.配置主机清单4.配置密钥对验证 三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9&a…

自定义一个仿拼多多地址选择器

前言 做了一个仿拼多多的地址选择器&#xff0c;但是与拼多多实现方法有些出入&#xff0c;大体效果是差不多的。废话不多说&#xff0c;先上一张效果动图&#xff1a; 开始 先说说本文的一些概念。地区级别&#xff1a;就是比如省级&#xff0c;市级&#xff0c;县级&#x…

map,set的封装(基于改造红黑树)

目录 引言 1.迭代器 2.map的[]重载 3.KeyOfValue模板参数 4.整体代码展示 //改造后的红黑树代码 #include <iostream> using namespace std;enum Colour {RED 0,BLACK, };//为了实现map与set封装使用同一个模板红黑树&#xff0c;前者的value是pair&#xff0c;后者…

WebAgent-基于大型语言模型的代理程序

大型语言模型&#xff08;LLM&#xff09;可以解决多种自然语言任务&#xff0c;例如算术、常识、逻辑推理、问答、文本生成、交互式决策任务。最近&#xff0c;LLM在自主网络导航方面也取得了巨大成功&#xff0c;代理程序助HTML理解和多步推理的能力&#xff0c;通过控制计算…

Spring——更快捷的存储 / 获取Bean对象

文章目录 前言一、存储 Bean 对象类注解为什么有五个类注解使用类注解存储对象配置扫描路径(重中之重)添加注解存储 Bean 对象 方法注解配置扫描路径(重中之重)使用方法注解存储对象 二、获取 Bean 对象Autowired属性注入Setter注入构造方法注入 Resource 总结 前言 本人是一个…

【雕爷学编程】MicroPython动手做(20)——掌控板之三轴加速度6

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

htmlCSS-----定位

目录 前言 定位 分类和取值 定位的取值 1.相对定位 2.绝对位置 元素居中操作 3.固定定位 前言 今天我们来学习html&CSS中的元素的定位&#xff0c;通过元素的定位我们可以去更好的将盒子放到我们想要的位置&#xff0c;下面就一起来看看吧&#xff01; 定位 定位posi…

rust 闭包函数

函数有自己的类型&#xff0c;可以像使用基础类型一样使用函数&#xff0c;包括将函数保存在变量中、保存在 vec 中、声明在结构体成员字段中。闭包函数也是函数&#xff0c;也有自己的类型定义。不过&#xff0c;函数实际上是指针类型&#xff0c;在 rust 所有权中属于借用的关…

Tomcat修改端口号

网上的教程都比较老&#xff0c;今天用tomcat9.0记录一下 conf文件夹下server.xml文件 刚开始改了打红叉的地方&#xff0c;发现没用&#xff0c;改了上面那行

SpringBoot百货超市商城系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBoot框架开发的百货超市系统。首先&#xff0c;这是一个很适合SpringBoot初学者学习的项目&#xff0c;代…