WebSocket 协议及其使用案例
文章目录
- 前言
- 一、初识 WebSocket 协议
- 1.1 什么是 WebSocket 协议
- 1.2 WebSocket 与 HTTP 的关系
- 1.3 WebSocket 握手的过程
- 1.4 WebSocket 解决了什么问题
- 二、WebSocket 数据帧格式
- 2.1 WebSocket 数据帧格式图示
- 2.2 各字段的详细说明
- 三、SpringBoot 项目中引入 WebSocket
- 3.1 创建 Spring Boot 项目并引入 WebSocket 依赖
- 3.2 继承 TextWebSocketHandler 类
- 3.3 配置资源路径
- 3.4 前端代码
- 3.5 运行演示
前言
WebSocket 协议是一种用于实时通信、全双工的网络协议。它运行在传输层之上,通常基于 TCP 连接。相较于传统的 HTTP 协议,WebSocket 协议能够在单个连接上进行双向通信
,实现数据的实时交互
,因此在实时性较高的应用中表现得尤为出色。
一、初识 WebSocket 协议
1.1 什么是 WebSocket 协议
WebSocket 协议是一种允许服务器和客户端之间进行全双工、实时通信的协议
。它克服了 HTTP 协议的请求-响应
模式,通过在客户端和服务器之间建立持久性的连接,实现了数据的实时传输。传统的 HTTP 连接每次请求都需要建立和关闭,而 WebSocket 连接则能够一直保持开放状态,避免了频繁的连接建立和关闭开销。
WebSocket协议的特点包括:
- 全双工通信: 服务器和客户端可以同时发送和接收数据,而不需要等待对方的请求或响应。
- 持久连接: WebSocket 连接保持开放状态,避免了频繁的连接建立和关闭,降低了通信延迟。
- 低延迟: 由于连接一直开放,数据可以实时传输,满足实时性需求。
- 更少的数据开销: 与 HTTP 相比,WebSocket 连接只需要在握手阶段进行一次 HTTP 请求,减少了数据传输的开销。
1.2 WebSocket 与 HTTP 的关系
WebSocket 协议和 HTTP 协议之间有密切的关系,WebSocket 连接的建立需要通过 HTTP 握手来协商
,其基本流程如下:
- 在握手阶段,客户端首先发送一个 HTTP 请求,并在请求报头中设置一个
Upgrade
字段,表示希望升级到WebSocket连接。 - 服务器在确认后,返回一个 HTTP 响应,即将协议切换为 WebSocket 协议,此时就表示升级成功了。
- 此后,连接从 HTTP 协议升级为 WebSocket 协议,实现了全双工、数据实时通信。
下图展示了建立 WebSocket 连接的基本过程:
1.3 WebSocket 握手的过程
WebSocket 的握手是指在建立 WebSocket 连接时,客户端和服务器之间通过 HTTP 协议进行交互,以协商升级连接到 WebSocket 协议。WebSocket的握手过程允许服务器和客户端确认其支持WebSocket协议,并进行连接的升级。
以下是 WebSocket 握手的基本步骤:
- 客户端发送握手请求: 客户端通过发送一个 HTTP 请求来启动 WebSocket 握手。这个请求中需要包含一些特定的头部字段,以及用于安全验证的
Sec-WebSocket-Key
字段。
示例请求头部:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- 服务器响应握手: 服务器接收到握手请求后,会验证
Sec-WebSocket-Key
字段,并生成一个响应密钥。如果验证通过,服务器会返回一个 HTTP 响应,表示升级到 WebSocket 协议。
示例响应头部:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- 连接升级: 一旦客户端收到服务器的响应,表示握手成功。此时连接会从HTTP协议升级到WebSocket协议,客户端和服务器可以开始在同一个连接上进行实时的双向通信。
需要注意的是,Sec-WebSocket-Key
和 Sec-WebSocket-Accept
字段的计算是基于一种安全验证的机制。Sec-WebSocket-Key
是客户端随机生成的一个字符串,服务器会使用这个字符串进行一定的计算,然后生成Sec-WebSocket-Accept
字段,以确保握手请求的合法性。
WebSocket 握手过程的成功完成意味着客户端和服务器之间已经建立了一个 WebSocket 连接,双方可以在这个连接上进行实时的数据传输。握手是 WebSocket 通信的起始点,之后的数据交换将遵循 WebSocket 的数据帧格式进行。
1.4 WebSocket 解决了什么问题
WebSocket 协议解决了传统 HTTP 协议在实时性、延迟和数据开销等方面存在的问题。通过保持持久性连接和全双工通信,WebSocket 协议实现了实时数据的传输,适用于需要及时通知和数据更新的应用,如:
- 即时聊天应用,用户能够实时收到新消息。
- 实时游戏,玩家可以同时看到其他玩家的动作。
- 股票市场监控,投资者能够及时了解股价变动。
总之,WebSocket 协议弥补了 HTTP 协议在实时性通信方面的不足,为实时应用提供了高效、低延迟的解决方案。在接下来的内容中,我们将深入探讨 WebSocket 协议的数据格式以及在 Spring Boot 项目中的应用。
二、WebSocket 数据帧格式
2.1 WebSocket 数据帧格式图示
2.2 各字段的详细说明
WebSocket协议的数据帧格式是用于在WebSocket连接上传输数据的基本单位。每个数据帧包含了控制信息和有效负载(Payload Data)。以下是WebSocket数据帧的格式及其各个字段的详细说明:
-
FIN (1 bit):表示消息是否是最后一个数据帧。若为1,则表示是消息的最后一个帧;若为0,则还有后续数据帧。
-
RSV1, RSV2, RSV3 (各占 1 bit):保留位,通常设置为0,用于未来的扩展。
-
Opcode (4 bits):指定数据帧的类型。常见的类型有:
- 0x0:表示数据帧是一个连续帧。
- 0x1:表示数据帧是文本帧。
- 0x2:表示数据帧是二进制帧。
- 0x8:表示连接关闭帧。
- 0x9:表示Ping帧,用于心跳检测。
- 0xA:表示Pong帧,作为对Ping的响应。
- 其他值为保留或用于扩展。
-
MASK (1 bit):表示Payload Data是否经过掩码加密。客户端发送给服务器的数据帧需要加密,所以MASK位为1。
-
Payload length (7 bits, 7+16 bits, 或 7+64 bits):表示Payload Data的长度。
- 若值在0~125之间,表示Payload Data的实际长度。
- 若值为126,则表示后续的16 bits为无符号整数,用于表示Payload Data的长度。
- 若值为127,则表示后续的64 bits为无符号整数,用于表示Payload Data的长度。
-
Extended payload length (16或64 bits):仅在Payload length为126或127时出现,用于表示实际的Payload Data长度。
-
Masking-key (32 bits):仅在MASK位为1时出现,用于解码Payload Data。Masking-key是一个32位的随机数。
-
Payload Data:有效负载数据。如果MASK位为1,则需要对Payload Data进行掩码解密,使用Masking-key进行解码。
WebSocket数据帧的格式允许在同一个连接上传输不同类型的数据,并且可以通过掩码保护数据的安全性。不同的Opcode类型用于标识数据的用途,例如文本数据、二进制数据、连接关闭、心跳检测等。
三、SpringBoot 项目中引入 WebSocket
3.1 创建 Spring Boot 项目并引入 WebSocket 依赖
引入 WebSocket 依赖非常简单,只需要在创建 SpringBoot 项目的时候,勾选上 WebSocket 就行了:
3.2 继承 TextWebSocketHandler 类
要使用 WebSocket 的相关功能,需要继承 TextWebSocketHandler
类来实现我们自己的 MyWebSocketHandler
类,我创建了一个websocket
包,在里面建立一个类 MyWebSocketHandler
:
然后需要重写里面的方法。这里我重新了四个方法:
这四个方法分别代表的含义:
-
afterConnectionEstablished
方法:在WebSocket连接成功建立后被自动调用。 -
handleTextMessage
方法:在WebSocket接收到文本消息时被自动调用。常常用于收到的消息进行转发。 -
handleTransportError
方法:在连接出现异常时被自动调用。 -
afterConnectionClosed
方法:在连接正常关闭后被自动调用。
通过实现这些方法,可以控制 WebSocket 连接的行为,包括连接的建立、消息的处理、异常的处理以及连接的关闭。这使得我们能够在应用中实现 WebSocket 通信的各种逻辑。
WebSocket 代码样例:
此处只是对连接建立、收到的消息、连接断开、异常等进行了简单的打印操作。
@Service
public class MyWebSocketHandler extends TextWebSocketHandler {/*** 这个方法会在 WebSocket 连接成功后,被自动调用** @param session WebSocket 连接中对应的会话* @throws Exception 异常信息*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.println("TestWebSocketComponent 连接成功!");}/*** 这个方法会在 WebSocket 收到消息的时候,被自动调用** @param session WebSocketSession* @param message 收到消息的值* @throws Exception 异常信息*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {System.out.println("TestWebSocketComponent 收到消息!" + message.toString());// session 是一个会话,其中记录了通信双方是谁(session 中就持有了 WebSocket 的通信连接)session.sendMessage(message);}/*** 这个方法会在连接出现异常的时候,被自动调用** @param session WebSocketSession* @param exception 异常信息* @throws Exception 异常信息*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {System.out.println("TestWebSocketComponent 连接异常了!");}/*** 这个方法会在连接正常关闭后,被自动调用** @param session WebSocketSession* @param status 关闭的状态* @throws Exception 异常信息*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {System.out.println("TestWebSocketComponent 连接关闭!");}
}
3.3 配置资源路径
要想使我们刚才创建的 MyWebSocketHandler
能够在建立 WebSocket 连接之后能够被调用,还需要对其进行配置,即注册该类与资源路径的映射关系。
- 首先创建一个
config
包,然后在里面创建一个类WebSocketConfig
。
- 这个类需要实现
WebSocketConfigurer
接口,并重写registerWebSocketHandlers
方法。
注意,需要添加 @Configuration
表示其是一个配置类,并且储存到 Spring 中;另外还需添加 @EnableWebSocket
注解,表示启用WebSocket 支持,使应用能够处理 WebSocket 连接。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {/*** 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中** @param registry WebSocketHandlerRegistry*/@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {}
}
- 最后将刚才的
MyWebSocketHandler
与一个具体的请求路径关联。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate MyWebSocketHandler myWebSocketHandler;/*** 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中** @param registry WebSocketHandlerRegistry*/@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 当浏览器通过 WebSocket 请求的路径是 '/test' 的时候,就会调用到 MyWebSocketHandler 这个类中的方法registry.addHandler(myWebSocketHandler, "/test");}
}
当建立了 WebSocket 连接并且请求的路径是 /test
的时候,就会调用到 MyWebSocketHandler
这个类中的方法。通过这个配置,将WebSocket 的处理逻辑和路径连接起来,使得 WebSocket 连接能够在应用中得到正确处理和响应。
3.4 前端代码
编写一个用于测试 WebSocket 连接的简单前端页面:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>测试 WebSocket 的使用</title>
</head>
<body><input type="text" id="message"><button id="send-button">发送</button><script>// 创建一个 WebSocket 实例,连接到服务器的 /test 路径let websocket = new WebSocket("ws://localhost:8081/test");// WebSocket 连接建立成功的回调函数websocket.onopen = function () {console.log("WebSocket 连接成功!");}// WebSocket 收到消息的回调函数websocket.onmessage = function (message) {console.log("WebSocket 收到消息:" + message.data);}// WebSocket 连接断开的回调函数websocket.onclose = function () {console.log("WebSocket 连接断开!");}// WebSocket 连接异常的回调函数websocket.onerror = function () {console.log("WebSocket 连接异常!");}// 获取页面元素let messageInput = document.querySelector("#message");let sendButton = document.querySelector('#send-button');// 发送按钮的点击事件sendButton.onclick = function(){console.log("WebSocket 发送消息:" + messageInput.value);websocket.send(messageInput.value); // 向服务器发送消息}</script>
</body>
</html>
这段代码创建了一个简单的 HTML 页面,其中包含一个输入框、一个发送按钮和一些用于 WebSocket 连接的 JavaScript 代码。具体来说:
- 页面加载时,JavaScript 代码创建了一个 WebSocket 实例,连接到了
ws://localhost:8081/test
路径。这与之前在WebSocketConfig
中注册的路径相对应。 - 设置了 WebSocket 的各种回调函数,分别处理连接建立、消息接收、连接断开和连接异常时的情况。
- 当用户在输入框中输入文本并点击发送按钮时,JavaScript代码会使用
websocket.send(messageInput.value)
将用户输入的消息发送给服务器。
这个前端页面用于与之前创建的 WebSocket 处理器进行通信,可以在浏览器中打开这个页面,然后在控制台中观察 WebSocket 连接状态和消息传输情况。
3.5 运行演示
启动SpringBoot项目,在浏览器中通过 http://localhost:8081/test.html
进行访问:
此时可以发现成功建立了 WebSocket 连接,并且可以通过抓包观察请求和响应。
下面是在建立 WebSocket 连接时,通过 Fiddle 抓包获取到的具体请求和响应:
请求:
响应:
页面中输入 “hello world”,然后发送:
此时可以看到,前端页面中成功通过 WebSocket 发送了消息。
后端输出:
发现此时后端也成功收到了刚才发送的消息。后续就可以将这个收到的消息进行转发,从而实现实时通信了。
相关文章:

WebSocket 协议及其使用案例
文章目录 前言一、初识 WebSocket 协议1.1 什么是 WebSocket 协议1.2 WebSocket 与 HTTP 的关系1.3 WebSocket 握手的过程1.4 WebSocket 解决了什么问题 二、WebSocket 数据帧格式2.1 WebSocket 数据帧格式图示2.2 各字段的详细说明 三、SpringBoot 项目中引入 WebSocket3.1 创…...

Java应用CPU占用过高故障排除
一、背景 最近测试反馈测试环境接口偶现有访问超时,然后APP提示是网络失败,看了一下测试环境的应用完全没啥问题,一直以为是网络问题。 今天测试有反馈了,赶紧看了一下测试服务器,这次终于有症状了,CPU直…...

嵌入式Linux开发实操(十五):nand flash接口开发(2)
通用NAND驱动程序支持几乎所有基于NAND的芯片,并将它们连接到Linux内核的内存技术设备(MTD)子系统。这个接口走的是nand的并口,可以在shell的/dev中看到设备,比如/mtd0、/mtd0ro…,mtdblock0、mtdblock1… sysfs在设备层次结构中提供了几个视角。设备必须挂在某条总线bus…...

作为一家游戏开发公司,有哪些经验可以分享?
在竞争激烈的游戏开发行业中,成功的游戏开发公司需要不断学习、创新和积累经验。作为一家经验丰富的游戏开发公司,我们愿意分享一些我们认为对于取得成功至关重要的经验和教训。这些经验涵盖了游戏开发的各个方面,从创意构思到发布和营销。希…...

【100天精通Python】Day51:Python 数据分析_数据分析入门基础与Anaconda 环境搭建
目录 1 科学计算和数据分析概述 2. 数据收集和准备 2.1 数据收集 2.1.1 文件导入: 2.1.2 数据库连接: 2.1.3 API请求: 2.1.4 网络爬虫: 2.2 数据清洗 2.2.1 处理缺失值: 2.2.2 去除重复值: 2.2…...

网络安全(黑客)自学路线
很多人上来就说想学习黑客,但是连方向都没搞清楚就开始学习,最终也只是会无疾而终!黑客是一个大的概念,里面包含了许多方向,不同的方向需要学习的内容也不一样。 算上从学校开始学习,已经在网安这条路上走…...

HTML5
写在前面 一、简单认识HTML 1.1 什么是网页【2023/08/31】 网站是指因特网上根据一定的规则,使用HTML等制作的用于展示特定内容相关的网页集合。 网页是网站中的一“页”,通常是HTML格式的文件,它要通过浏览器来阅读。 网页是构成网站的…...

Vue+Element-ui实现表格本地导入
表格文件存储在前端 如图,表格文件template.xlsx存储在public下的static文件夹下 注意这里的路径容易报错 a链接下载文件失败的问题(未发现文件) a.href ‘./static/template.xlsx’ 写的时候不能带public,直接这么写就可以 DownloadTemp…...

Golang参数输入
Golang参数输入 1.命令行参数(os.Args) package mainimport ("fmt""os""strconv" )func main() {for i, num : range os.Args[1:] {fmt.Println("参数"strconv.Itoa(i)": ", num)} } //输入&#x…...

2023年8月第4周大模型荟萃
2023年8月第4周大模型荟萃 2023.8.31版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、美国法官最新裁定:纯AI生成的艺术作品不受版权保护 美国华盛顿一家法院近日裁定,根据美国政府的法律,在没有任何…...

Kafka监控工具,LinkedIn详解
Kafka监控工具包括以下几种: Kafka Manager:这是一个开源的Kafka集群管理工具,可以监控Kafka集群的健康和性能,并提供可视化的用户界面。 Kafka Monitor:这是LinkedIn开发的一个监控工具,可以监控Kafka集群…...

如何处理 Flink 作业频繁重启问题?
分析&回答 Flink 实现了多种重启策略 固定延迟重启策略(Fixed Delay Restart Strategy)故障率重启策略(Failure Rate Restart Strategy)没有重启策略(No Restart Strategy)Fallback重启策略ÿ…...

Windows 安装 RabbitMq
Windows 上安装 RabbitMQ 的步骤 RabbitMQ 是一个强大的开源消息队列系统,广泛用于构建分布式、可扩展的应用程序。本教程将带您一步一步完成在 Windows 系统上安装 RabbitMQ 的过程。无需担心,即使您是初学者,也能够轻松跟随这些简单的步骤…...

Mybatis的关系关联配置
前言 MyBatis是一个流行的Java持久化框架,它提供了一种简单而强大的方式来映射Java对象和关系数据库之间的数据。在MyBatis中,关系关联配置是一种用于定义对象之间关系的方式,它允许我们在查询数据库时同时获取相关联的对象。 在MyBatis中&…...

【知识积累】准确率,精确率,召回率,F1值
二分类的混淆矩阵(预测图片是否是汉堡) 分类器到底分对了多少? 预测的图片中正确的有多少? 有多少张应该预测为是的图片没有找到? 精确率和召回率在某种情况下会呈现此消彼长的状况。举个极端的例子…...

什么是分布式系统?
分布式系统是由多个独立的计算机或计算节点组成的系统,这些节点通过消息传递或共享数据的方式进行协调和通信,以实现共同的目标。分布式系统的设计目标是提高系统的可靠性、可扩展性、性能和容错性。 在一个分布式系统中,各个计算机节点之间…...

[AGC043D] Merge Triplets
题目传送门 引 很有意思的计数题 解法 考虑经过操作后得到的排列的性质 性质1: 设 p r e ( i ) pre(i) pre(i):前i个位置的最大值,则不会出现超过3个的连续位置的 p r e pre pre相同 必要性: 考虑反证,若有超过 3 3 3个的连续…...

2023年人工智能开源项目前20名
推荐:使用 NSDT场景编辑器快速搭建3D应用场景 1. Tensorflow 2. Hugging Face Transformers 3. Opencv 4. Pytorch 5. Keras 6. Stable Diffusion 7. Deepfacelab 8. Detectron2 9. Apache Mxnet 10. Fastai 11. Open Assistant 12. Mindsdb 13. Dall E…...

ThinkPHP 集成 jwt 技术 token 验证
ThinkPHP 集成 jwt 技术 token 验证 一、思路流程二、安装 firebase/php-jwt三、封装token类四、创建中间件,检验Token校验时效性五、配置路由中间件六、写几个测试方法,通过postman去验证 一、思路流程 客户端使用用户名和密码请求登录服务端收到请求&…...

gerrit 如何提交进行review
前言 本文主要介绍如何使用gerrit进行review。 下述所有流程都是参考: https://gerrit-review.googlesource.com/Documentation/intro-gerrit-walkthrough.html 先给一个commit后但是还没有push上去的一个办法: git reset --hard HEAD^可以多次reset.…...

罗勇军 →《算法竞赛·快冲300题》每日一题:“游泳” ← DFS+剪枝
【题目来源】http://oj.ecustacm.cn/problem.php?id1753http://oj.ecustacm.cn/viewnews.php?id1023【题目描述】 游泳池可以等分为n行n列的小区域,每个区域的温度不同。 小明现在在要从游泳池的左上角(1, 1)游到右下角(n, n),小明只能向上下左右四个方…...

【教程】PyTorch Timer计时器
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] OpenCV的Timer计时器可以看这篇:Python Timer和TimerFPS计时工具类 Timer作用说明:统计某一段代码的运行耗时。 直接上代码,开箱即用。 import time import torch import os …...

因果推断(六)基于微软框架dowhy的因果推断
因果推断(六)基于微软框架dowhy的因果推断 DoWhy 基于因果推断的两大框架构建:「图模型」与「潜在结果模型」。具体来说,其使用基于图的准则与 do-积分来对假设进行建模并识别出非参数化的因果效应;而在估计阶段则主要…...

探索隧道ip如何助力爬虫应用
在数据驱动的世界中,网络爬虫已成为获取大量信息的重要工具。然而,爬虫在抓取数据时可能会遇到一些挑战,如IP封禁、访问限制等。隧道ip(TunnelingProxy)作为一种强大的解决方案,可以帮助爬虫应用更高效地获…...

题目:2629.复合函数
题目来源: leetcode题目,网址:2629. 复合函数 - 力扣(LeetCode) 解题思路: 倒序遍历计算。 解题代码: /*** param {Function[]} functions* return {Function}*/ var compose function(…...

【实训项目】精点考研
1.设计摘要 如果说高考是一次能够改变命运的考试,那么考研应该是另外一次。为什么那么多人都要考研呢?从中国教育在线官方公布是考研动机调查来看,大家扎堆考研的原因大概集中在这6个方面:本科就业压力大,提升竞争力、…...

软件测试Pytest实现接口自动化应该如何在用例执行后打印日志到日志目录生成日志文件?
Pytest可以使用内置的logging模块来实现接口自动化测试用例执行后打印日志到日志目录以生成日志文件。以下是实现步骤: 1、在pytest配置文件(conftest.py)中,定义一个日志输出路径,并设置logging模块。 import loggi…...

深入理解作用域、作用域链和闭包
🎬 岸边的风:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 📚 前言 📘 1. 词法作用域 📖 1.2 示例 📖 1.3 词法作用域的…...

7款适合3D建模和渲染的GPU推荐
选择一款完美的 GPU 并不是一件容易的事;您不仅必须确保有特定数量的线程和内核来处理图像,而且还应该有足够的 RAM。 这是因为 3D 渲染是一个活跃的工作过程,因为您必须坐在 PC 前并持续与软件交互。为了在 3D 场景中积极工作,您…...

边缘计算物联网网关在机械加工行业的应用及作用分享
随着工业4.0的推进,物联网技术正在逐渐渗透到各个行业领域。机械加工行业作为制造业的基础领域之一,其生产过程的自动化、智能化水平直接影响到产品质量和生产效率。边缘计算物联网网关作为物联网技术的重要组成部分,在机械加工行业中发挥着越…...