WebSocket 协议的原理和实践

WebSocket 是一种基于 TCP 的双向通信协议,它可以在客户端和服务器之间建立一个持久连接,并且可以在连接生命周期内进行数据的传输。WebSocket 协议的目的是为了解决 HTTP 协议的一些局限性,比如:

  • HTTP 协议是单向的:HTTP 协议是基于请求-响应的模式,客户端只能主动发起请求,服务器只能被动响应,不能主动推送数据给客户端,这导致了数据的不及时和资源的浪费。
  • HTTP 协议是无状态的:HTTP 协议是无状态的,每次请求都是独立的,服务器无法识别客户端的身份和状态,需要借助于 Cookie、Session 等机制来维持连接的状态,这增加了复杂度和开销。
  • HTTP 协议是文本的:HTTP 协议是基于文本的,请求和响应的格式都是由 ASCII 字符组成的,这导致了数据的冗余和低效,尤其是对于二进制数据的传输,需要进行编码和解码,增加了计算量和传输量。

WebSocket 协议通过以下几个方面来克服 HTTP 协议的局限性:

  • WebSocket 协议是双向的:WebSocket 协议是基于帧的模式,客户端和服务器都可以主动发送和接收数据帧,不需要等待对方的请求或响应,实现了真正的双向通信,提高了数据的实时性和效率。
  • WebSocket 协议是有状态的:WebSocket 协议是有状态的,客户端和服务器之间只需要建立一次握手,就可以建立一个持久的连接,服务器可以识别客户端的身份和状态,不需要使用 Cookie、Session 等机制来维持连接的状态,简化了逻辑和开销。
  • WebSocket 协议是二进制的:WebSocket 协议是基于二进制的,请求和响应的格式都是由字节组成的,这减少了数据的冗余和低效,尤其是对于二进制数据的传输,不需要进行编码和解码,减少了计算量和传输量。

WebSocket 协议的握手过程

WebSocket 协议的握手过程是基于 HTTP 协议的,客户端和服务器之间需要先通过 HTTP 协议进行一次握手,然后升级为 WebSocket 协议,建立一个持久的连接。握手过程的具体步骤如下:

  • 客户端发起握手请求:客户端通过 HTTP 协议向服务器发送一个 GET 请求,请求头中包含以下几个字段:
    • Upgrade:表示要升级的协议,值为 websocket。
    • Connection:表示要保持的连接类型,值为 Upgrade。
    • Sec-WebSocket-Key:表示一个随机的字符串,用于服务器生成响应的校验码。
    • Sec-WebSocket-Version:表示 WebSocket 协议的版本,值为 13。
    • Origin:表示请求的来源,用于服务器验证和过滤。
    • Sec-WebSocket-Protocol:表示客户端支持的子协议,用于服务器选择和协商。
    • Sec-WebSocket-Extensions:表示客户端支持的扩展,用于服务器选择和协商。
  • 服务器响应握手请求:服务器收到客户端的握手请求后,如果同意升级为 WebSocket 协议,就会发送一个 HTTP 响应,响应头中包含以下几个字段:
    • Upgrade:表示要升级的协议,值为 websocket。
    • Connection:表示要保持的连接类型,值为 Upgrade。
    • Sec-WebSocket-Accept:表示一个校验码,由服务器根据客户端的 Sec-WebSocket-Key 生成,用于客户端验证服务器的身份。
    • Sec-WebSocket-Protocol:表示服务器选择的子协议,必须是客户端请求中包含的子协议之一。
    • Sec-WebSocket-Extensions:表示服务器选择的扩展,必须是客户端请求中包含的扩展之一。
  • 客户端验证服务器响应:客户端收到服务器的响应后,需要验证服务器的身份,方法是根据自己的 Sec-WebSocket-Key 和一个固定的字符串,按照一定的算法,生成一个期望的校验码,然后和服务器的 Sec-WebSocket-Accept 进行比较,如果相同,表示服务器的身份有效,握手成功;如果不同,表示服务器的身份无效,握手失败,关闭连接。

WebSocket 协议的数据帧格式

WebSocket 协议的数据是通过一系列的帧(frame)来进行传输的,每个帧由头部(header)和负载(payload)两部分构成。帧的头部包含了一些控制位和长度字段,用于指示帧的类型、大小和边界。帧的负载是实际传输的数据,长度由头部指示,内容由类型指示,如果有掩码,需要进行异或运算。具体来说,帧的头部有以下几个字段:

  • FIN:1 位,表示是否是最后一个帧,如果是 1,表示这是消息的最后一个帧;如果是 0,表示后面还有更多的帧。
  • RSV1, RSV2, RSV3:各占 1 位,保留位,用于扩展,一般为 0,除非协商另外的含义:
    • 压缩扩展:这种扩展可以用于对数据帧进行压缩,以减少传输的数据量,提高效率。比如 permessage-deflate 扩展,它使用了 RSV1 位来标识数据帧是否经过了 deflate 压缩算法,如果 RSV1 为 1,表示数据帧已经压缩,需要解压缩;如果 RSV1 为 0,表示数据帧没有压缩,不需要解压缩。
    • 多路复用扩展:这种扩展可以用于在一个 TCP 连接上建立多个 WebSocket 逻辑通道,以支持多个并发的数据流,提高并发性。比如 multiplexing 扩展,它使用了 RSV1 位来标识数据帧是否属于一个新的通道,如果 RSV1 为 1,表示数据帧是一个新通道的第一个帧,需要创建一个新的通道;如果 RSV1 为 0,表示数据帧是一个已有通道的后续帧,不需要创建新的通道。
    • 分片扩展:这种扩展可以用于对数据帧进行分片,以支持大数据量的传输,提高可靠性。比如 fragmentation 扩展,它使用了 RSV1 位来标识数据帧是否是一个分片的开始,如果 RSV1 为 1,表示数据帧是一个分片的开始,需要记录分片的信息;如果 RSV1 为 0,表示数据帧是一个分片的中间或结束,不需要记录分片的信息。
  • Opcode:4 位,表示帧的类型,可以是以下几种:
    • 0x0:表示一个延续帧,用于分片传输的消息的中间帧。
    • 0x1:表示一个文本帧,用于传输文本数据。
    • 0x2:表示一个二进制帧,用于传输二进制数据。
    • 0x8:表示一个关闭帧,用于关闭连接。
    • 0x9:表示一个心跳帧(ping),用于保持连接。
    • 0xA:表示一个心跳帧(pong),用于响应 ping。
  • Mask:1 位,表示是否有掩码,如果是 1,表示负载数据需要用一个 32 位的掩码进行异或运算,以增加安全性;如果是 0,表示负载数据不经过掩码处理。客户端发出的帧必须设置掩码位,而服务器发出的帧必须清除掩码位。
  • Payload length:7 位或 7+16 位或 7+64 位,表示负载数据的长度,如果值在 0-125 之间,那么就是负载数据的长度;如果值是 126,那么后面 2 个字节表示负载数据的长度;如果值是 127,那么后面 8 个字节表示负载数据的长度。
  • Masking-key:0 或 4 字节,表示掩码,如果 Mask 位是 1,那么这里是 4 个字节的掩码;如果 Mask位是 0,那么这里没有掩码。
  • Payload data:表示负载数据,长度由 Payload length 字段指示,内容由 Opcode 字段指示,如果 Mask 位是 1,那么需要用 Masking-key 字段的掩码进行异或运算。

业务层心跳机制

WebSocket 协议虽然自带了心跳ping和pong帧,但是这些帧只能用于检测TCP连接的存活,不能保证应用层的可用性。因此,有些业务场景下,还需要自行实现心跳机制,以实现以下目的

  • 保证连接的稳定性:有时候,即使 TCP 连接是正常的,但是中间的网络设备可能会对空闲的连接进行关闭,导致连接的断开。比如某些防火墙或者代理服务器可能会对长时间没有数据交换的连接进行主动关闭,如果使用 WebSocket 自身的机制无法防止这种情况,可能会导致客户端和服务器之间的连接不稳定,影响用户的体验。
  • 保证应用层的可用性:有时候,即使 TCP 连接是正常的,但是应用层可能出现故障,导致无法处理业务请求。比如某台服务器因为某些原因导致负载超高,CPU飙高,或者线程池打满等等,无法响应任何业务请求,如果使用 WebSocket 自身的机制无法发现任何问题,然而对客户端而言,这时的最好选择就是断连后重新连接其他服务器,而不是一直认为当前服务器是可用状态,向当前服务器发送一些必然会失败的请求。
  • 保证数据的实时性:有时候,即使 TCP 连接是正常的,但是网络层可能出现延迟,导致数据的传输不及时。比如某个路由器或者交换机出现故障,导致数据包的丢失或者重传,如果使用 WebSocket 自身的机制无法感知到这种情况,可能会导致客户端收到的数据是过期的或者不完整的,影响业务的正确性。

业务代码中自行实现心跳机制,可以根据不同的业务需求,定制不同的心跳策略,比如心跳的频率、内容、超时时间等,以达到更好的效果。

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

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

相关文章

自己动手写数据库: select 查询语句对应查询树的构造和执行

首先我们需要给原来代码打个补丁,在SelectScan 结构体初始化时需要传入 UpdateScan 接口对象,但很多时候我们需要传入的是 Scan 对象,因此我们需要做一个转换,也就是当初始化 SelectScan 时,如果传入的是 Scan 对象&am…

VUE笔试题精讲1

vue专题| ProcessOn免费在线作图,在线流程图,在线思维导图 VUE面试题视频 01-Vue组件之间通信方式有哪些? 1. 组件通信常⽤⽅式有以下8种: props $emit/$on $children/$parent $attrs/$listeners ref $root eventbus vuex 注意vue3中废弃的⼏个API https://v3-mig…

Python学习之——装饰器

Python学习之——装饰器 参考基础闭包概念装饰器系统自带的装饰器propertystaticmethodclassmethod 自定义装饰器函数的装饰器无参数有参数 类的装饰器无参数有参数 functools.wraps装饰器类 装饰器实现单例模式 参考 python装饰器的4种类型:函数装饰函数、函数装饰…

全志V3s之U-Boot

1、安装交叉编译器: ARM交叉编译器的官网:交叉编译器 a、使用wget下载: wget https://releases.linaro.org/components/toolchain/binaries/latest/arm-linux-gnueabihf/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xzb、解…

【运动控制】移动机器人的移动效率/运动效率包括的性能

速度 (Speed): 机器人在单位时间内的位移。 灵活性 (Agility): 机器人在不同方向上的灵活程度。 稳定性 (Stability): 机器人运动过程中的平稳性和不摇晃的程度。 准确性 (Accuracy): 机器人达到目标位置的精确程度。 能耗 (Energy Efficiency): 机器人完成任务所消耗的能源…

mmseg上手自己的数据集

制作自己的数据集,VOC格式为例。 这三个文件包括数据集的名称。可以使用labelme脚本自动生成。 跟据预测类别修改配置文件 D:\projects\mmsegmentation-main\mmseg\datasets\voc.py 因为是voc格式的数据集,在这个文件里进行配置,修改成自己数…

每日分享,以元旦为题的诗词

元旦佳节即将来临,相信大家都会在朋友圈表达一下自己的情感,不管大家以前是怎么表达的,今天小编给你分享几首以元旦为题的几首诗,喜欢的朋友可以自取,想要更多免费的诗词,请自行百度或小程序搜索&#xff1…

异构集成的高速总线核算实例

带宽增长的问题 Shannon-Hartley定理给出了一个无线信道能够传输的信息总量 C BW log2(1SNR) 其中C为信道容量,BW为信号带宽,SNR为信道的信噪比,信息容量直接与信道带宽成正比。而信道带宽则直接由采样速率所确定。…

SLAM算法与工程实践——相机篇:传统相机使用(2)

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接,本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址: SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…

C++共享和保护——(3)静态成员

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 信念,你拿它没办法&#x…

【算法集训】基础数据结构:七、树

第一题 2236. 判断根结点是否等于子结点之和 这一题很简单,只有三个节点,判断就可以了 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/bool checkTree(s…

Java 第8章 本章作业

目录 4.通过继承实现员工工资核算打印功能 6.父类和子类中通过this和super都可以调用哪些属性和方法 8.扩展如下的BankAccount类 10.判断测试类中创建的两个对象是否相等 11.向上转型&向下转型 12.equals和的区别 15.什么是多态,多态具体体现有哪些? 16. java的动…

Spring Bean基础

写在最前面: 本文运行的示例在我github项目中的spring-bean模块,源码位置: spring-bean 前言 为什么要先掌握 Spring Bean 的基础知识? 我们知道 Spring 框架提供的一个最重要也是最核心的能力就是管理 Bean 实例。以下是其原因: 核心组件…

Python实现图像批量png格式转为npy格式

前言 在进行深度学习处理时,有些的代码处理的数据格式为npy,但是常常有的数据格式为png,因此下面就用Python实现图像批量png格式转为npy格式,代码进行了详细注释。 1 png2npy # -- coding: UTF-8 --Author: Clouds rising Date…

新版Spring Security6.2案例 - Authentication用户名密码

前言: 前面有翻译了新版Spring Security6.2架构,包括总体架构,Authentication和Authorization,感兴趣可以直接点链接,这篇翻译官网给出的关于Authentication的Username/Password这页。 首先呢,官网就直接…

前端如何使用express写一个简单的服务

相信不少前端平常在日常工作中肯遇见过后端API接口没开发出来的时候吧 前端提升小技巧 自己使用nodejs——express ,koa,egg开发接口吧(本人比较喜欢egg和express) 今天先分享一下express 下面是一个简单的demo 1、首先咱们可以新建一个文件夹,创建一个app.js 下…

【开源软件】最好的开源软件-2023-第18名 OpenTelemetry

自我介绍 做一个简单介绍,酒架年近48 ,有20多年IT工作经历,目前在一家500强做企业架构.因为工作需要,另外也因为兴趣涉猎比较广,为了自己学习建立了三个博客,分别是【全球IT瞭望】,【…

扫雷/python中*解包作用

题目描述 在一个n行m列的方格图上有一些位置有地雷,另外一些位置为空。 请为每个空位置标一个整数,表示周围八个相邻的方格中有多少个地雷。 输入描述 输入的第一行包含两个整数n,m。 第2行到第n1行每行包含m个整数,相邻整数之间用-个空格分隔…

Keepalived+Nginx实现高可用(下)

一、背景 上篇文章介绍了基本的Keepalived的简单入门,但是针对预留的问题还有优化的空间。分别是下面3个问题: 1、如果仅仅只提供一个VIP的方式,会存在只有1台服务器处于实际工作,另外1台处于闲置状态。 势必存在成本资源浪费问题&#xff0c…

定时器Timer、多线程下的单例模式

在java中,Timer主要负责计划任务的功能,即在指定的时间开始执行某个任务。TimerTask是一个抽象类,负责封装定时任务。 1、定时器Timer的使用 schedule(TimerTask task, Date date):在指定的日期执行一次task。 schedule(TimerTask…