WebSocket 协议及其使用案例

news2024/11/19 15:27:53

文章目录

  • 前言
  • 一、初识 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 握手的基本步骤:

  1. 客户端发送握手请求: 客户端通过发送一个 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
  1. 服务器响应握手: 服务器接收到握手请求后,会验证 Sec-WebSocket-Key 字段,并生成一个响应密钥。如果验证通过,服务器会返回一个 HTTP 响应,表示升级到 WebSocket 协议。

示例响应头部:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  1. 连接升级: 一旦客户端收到服务器的响应,表示握手成功。此时连接会从HTTP协议升级到WebSocket协议,客户端和服务器可以开始在同一个连接上进行实时的双向通信。

需要注意的是,Sec-WebSocket-KeySec-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

然后需要重写里面的方法。这里我重新了四个方法:

这四个方法分别代表的含义:

  1. afterConnectionEstablished 方法:在WebSocket连接成功建立后被自动调用。

  2. handleTextMessage 方法:在WebSocket接收到文本消息时被自动调用。常常用于收到的消息进行转发。

  3. handleTransportError 方法:在连接出现异常时被自动调用。

  4. afterConnectionClosed 方法:在连接正常关闭后被自动调用。

通过实现这些方法,可以控制 WebSocket 连接的行为,包括连接的建立、消息的处理、异常的处理以及连接的关闭。这使得我们能够在应用中实现 WebSocket 通信的各种逻辑。

WebSocket 代码样例:

此处只是对连接建立、收到的消息、连接断开、异常等进行了简单的打印操作。

@Service
public class MyWebSocketHandler extends TextWebSocketHandler {
    /**
     * 这个方法会在 WebSocket 连接成功后,被自动调用
     *
     * @param session WebSocket 连接中对应的会话
     * @throws Exception 异常信息
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("TestWebSocketComponent 连接成功!");
    }

    /**
     * 这个方法会在 WebSocket 收到消息的时候,被自动调用
     *
     * @param session WebSocketSession
     * @param message 收到消息的值
     * @throws Exception 异常信息
     */
    @Override
    protected 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 异常信息
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("TestWebSocketComponent 连接异常了!");
    }

    /**
     * 这个方法会在连接正常关闭后,被自动调用
     *
     * @param session WebSocketSession
     * @param status  关闭的状态
     * @throws Exception 异常信息
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("TestWebSocketComponent 连接关闭!");
    }
}

3.3 配置资源路径

要想使我们刚才创建的 MyWebSocketHandler 能够在建立 WebSocket 连接之后能够被调用,还需要对其进行配置,即注册该类与资源路径的映射关系。

  1. 首先创建一个 config包,然后在里面创建一个类 WebSocketConfig
  2. 这个类需要实现 WebSocketConfigurer 接口,并重写 registerWebSocketHandlers 方法。

注意,需要添加 @Configuration 表示其是一个配置类,并且储存到 Spring 中;另外还需添加 @EnableWebSocket 注解,表示启用WebSocket 支持,使应用能够处理 WebSocket 连接。

@Configuration
@EnableWebSocket 
public class WebSocketConfig implements WebSocketConfigurer {
    /**
     * 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中
     *
     * @param registry WebSocketHandlerRegistry
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    }
}
  1. 最后将刚才的 MyWebSocketHandler 与一个具体的请求路径关联。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private MyWebSocketHandler myWebSocketHandler;

    /**
     * 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中
     *
     * @param registry WebSocketHandlerRegistry
     */
    @Override
    public 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 发送了消息。

后端输出:

发现此时后端也成功收到了刚才发送的消息。后续就可以将这个收到的消息进行转发,从而实现实时通信了。

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

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

相关文章

Meta语言模型LLaMA解读:模型的下载部署与运行代码

文章目录 llama2体验地址模型下载下载步骤准备工作什么是Git LFS下载huggingface模型 模型运行代码 llama2 Meta最新语言模型LLaMA解读&#xff0c;LLaMA是Facebook AI Research团队于2023年发布的一种语言模型&#xff0c;这是一个基础语言模型的集合。 体验地址 体验地址 …

分布式锁实现一. 利用Mysql数据库update锁

文章目录 分布式锁1、什么是分布式锁&#xff1a;2、分布式锁应该具备哪些条件&#xff1a; 基于数据库的分布式锁代码传送代码运行 分布式锁 1、什么是分布式锁&#xff1a; 分布式锁&#xff0c;即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题…

hp惠普光影精灵5笔记本HP Pavilion Gaming-15-dk0135tx原装出厂Win10系统

原厂系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、惠普电脑管家等预装程序 适用型号&#xff1a; 15-dk0011tx,15-dk0018tx,15-dk0019tx,15-dk0020tx,15-dk0021tx,15-dk0038tx 15-dk0039tx,15-dk0040tx,15-dk0041tx,15-dk0125tx,15-dk0126tx,15-dk0127tx 15-dk012…

Python Opencv实践 - 霍夫线检测(Hough Lines)

import cv2 as cv import numpy as np import matplotlib.pyplot as plt import randomimg cv.imread("../SampleImages/GreenBoard.jpg") print(img.shape) plt.imshow(img[:,:,::-1])#将图像转为二值图 gray cv.cvtColor(img, cv.COLOR_BGR2GRAY) plt.imshow(gra…

【【萌新的STM32学习25--- USART寄存器的介绍】】

萌新的STM32学习25- USART寄存器的介绍 STM32–USART寄存器介绍&#xff08;F1&#xff09; 控制寄存器1 &#xff08;CR1&#xff09; 位13&#xff1a; 使能USART UE 0&#xff1a; USART分频器和输出被禁止 1&#xff1a; USART模块使能 位12 &#xff1a; 配置8个数据位…

正中优配:股票xd什么意思

作为本钱市场中的重要一环&#xff0c;股票出资已经成为现代个人和组织出资的重要挑选之一。而在股票出资中&#xff0c;出资者常常会看到股票的价格上出现"xd"字样&#xff0c;这时候不少出资者就会疑问&#xff0c;这个"xd"到底是什么意思&#xff1f;对…

Java“牵手”1688整店商品API接口数据,通过店铺ID获取整店商品详情数据,1688店铺所有商品API申请指南

1688平台店铺所有商品数据接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取1688整店的商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、价格信息等详细信息 。 获取店铺所有商品接口API是一种用于获取电商平台上商品详…

骨传导耳机音质怎么样、十大公认音质好的骨传导耳机推荐

骨传导耳机的音质相比传统的耳塞式或耳罩式耳机会有所不同。下面是骨传导耳机音质和入耳式耳机音质的区别&#xff1a; 1、低频听感&#xff1a; 相对传统耳机而言&#xff0c;骨传导耳机的低频响应可能稍显不足。由于声音是通过振动传输到内耳&#xff0c;而不是通过直接振动…

Python 实战之ChatGPT + Python 实现全自动数据处理/可视化详解

本文目录 一、引言 二、成果演示——口述式数据可视化 三、远原理述 四、实现过程 &#xff08;一&#xff09;环境配置 &#xff08;二&#xff09;申请OpenAI账号 &#xff08;一&#xff09;调用ChatGPT API &#xff08;二&#xff09;设计AI身份&#xff0c;全自动处理数据…

基于食肉植物算法优化的BP神经网络(预测应用) - 附代码

基于食肉植物算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于食肉植物算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.食肉植物优化BP神经网络2.1 BP神经网络参数设置2.2 食肉植物算法应用 4.测试结果&#xff1a;5…

GPU编程(基于Python和CUDA)(二)——显示GPU信息

系列文章目录 GPU编程&#xff08;基于Python和CUDA&#xff09;&#xff08;一&#xff09;——零基础安装pycuda GPU编程&#xff08;基于Python和CUDA&#xff09;&#xff08;二&#xff09;——显示GPU信息 显示GPU信息 系列文章目录前言通过CUDA查看GPU信息使用pycuda查…

使用openpyxl来创建一个月的日程表

首先你心里要有一张表的样子&#xff0c;openpyxl才能帮你创建出其余的29张。 import openpyxl from openpyxl.styles import Alignment, Font import calendar from datetime import datework_path rXX\YY\ZZ\日报-九月.xlsxtry:workbook openpyxl.load_workbook(work_path…

The Annotated Transformer(Attention Is All You Need)

"Attention is All You Need"[1] 一文中提出的Transformer网络结构最近引起了很多人的关注。Transformer不仅能够明显地提升翻译质量&#xff0c;还为许多NLP任务提供了新的结构。虽然原文写得很清楚&#xff0c;但实际上大家普遍反映很难正确地实现。 所以我们为此…

不使用VH6501设备,通过VN1630等普通设备使用canConfigureBusOff函数进行busoff干扰测试

** 特别注意一下,使用这个函数需要你的vector驱动在9.6以上以及支持 ISO CAN FD. ** 函数canConfigureBusOff 可以通过脚本的形式产生bus off,而VH6501可以通过干扰bit位来产生bus off(使用CANoe Demo - CANDisturbanceMain进行Bus Off测试)。 对于函数canConfigureBusOf…

Cypress web自动化windows环境npm安装Cypress

前言 web技术已经进化了&#xff0c;web的测试技术最终还是跟上了脚步&#xff0c;新一代的web自动化技术出现了&#xff1f; Cypress可以对在浏览器中运行的任何东西进行快速、简单和可靠的测试。 官方地址https://www.cypress.io/,详细的文档介绍https://docs.cypress.io/g…

openGauss学习笔记-56 openGauss 高级特性-DCF

文章目录 openGauss学习笔记-56 openGauss 高级特性-DCF56.1 架构介绍56.2 功能介绍56.3 使用示例 openGauss学习笔记-56 openGauss 高级特性-DCF DCF全称是Distributed Consensus Framework&#xff0c;即分布式一致性共识框架。DCF实现了Paxos、Raft等解决分布式一致性问题典…

[SpringBoot3]远程访问@HttpExchange

六、远程访问HttpExchange[SpringBoot3] 远程访问是开发的常用技术&#xff0c;一个应用能够访问其他应用的功能。SpringBoot提供了多种远程访问的技术。基于HTTP协议的远程访问是最广泛的。SpringBoot中定义接口提供HTTP服务。生成的代理对象实现此接口&#xff0c;代理对象实…

C语言入门篇(九)

前言   本篇分享的是部分操作符的概念与用法&#xff0c;从经典例题入手&#xff0c;带你快速了解和掌握。   收录专栏&#xff1a;浅谈C语言 操作符详解下 10. 逗号表达式11. 下标引用、函数调用和结构成员12. 表达式求值12.1 隐士类型转换12.2 算术转换12.3 操作符的属性…

海面漂浮物垃圾识别检测算法

海面漂浮物垃圾识别检测算法通过yolo系列网络框架模型算法&#xff0c;海面漂浮物垃圾识别检测算法一旦识别到海面的漂浮物垃圾&#xff0c;海面漂浮物垃圾识别检测算法立即发出预警信号。目标检测架构分为两种&#xff0c;一种是two-stage&#xff0c;一种是one-stage&#xf…

MFC -- Date Time Picker 控件使用

当前环境&#xff1a;VS2015 Windows 10 //&#xff08;一&#xff09;使用普通函数&#xff0c; 获取当前时间CString strCurrentTime; COleDateTime m_time COleDateTime::GetCurrentTime(); strCurrentTime m_time.Format(_T("%Y-%m-%d %H:%M:%S")); SetDlgIt…