详解WebSocket

2023-08-03 11:56:51

目录

1.WebSocket是什么?

2.WebSocket的通信过程

3.WebSocket的报文结构

4.JAVA中的WebSocket


1.WebSocket是什么?

在传统的BS体系中,请求响应一直是单向的,服务器一直扮演的”被动“的角色,浏览器发起请求去访问服务器,服务器才会返回响应。这种单向的模式让实时通信、消息推送一类的场景,实现起来成本巨大。

HTML5里面提出了WebSocket标准,目的就是让服务器具有”主动“的能力,能由服务器向浏览器主动推送东西。WebSocket 是一种基于 TCP 协议的应用层协议,它允许客户端和服务器之间建立持久连接,实现实时通信和推送功能,其和HTTP属于并列的关系:

 

2.WebSocket的通信过程

WebSocket的通信分为两个阶段:

  • 握手阶段

  • 数据交换阶段

握手阶段:

  • 客户端通过 HTTP 请求发起握手请求,请求头包含一些特殊的字段,如pgrade: websocket和 Connection: Upgrade,以及其他的 WebSocket 相关字段。

  • 服务器响应握手:服务器收到客户端的 WebSocket 握手请求后,进行协议升级,将 HTTP 连接升级为 WebSocket 连接。服务器返回一个 WebSocket 握手响应,响应头中包含特殊字段,如 Upgrade: websocketConnection: Upgrade,以及其他的 WebSocket 相关字段。

  • 连接建立:一旦客户端收到服务器的 WebSocket 握手响应,WebSocket 连接就建立成功,现在客户端和服务器都可以发送和接收 WebSocket 消息了。

数据交换阶段:

  • 双向数据交换:WebSocket 连接建立后,客户端和服务器可以通过 WebSocket 会话进行双向的数据交换。任何一方都可以随时发送消息给对方,而不需要事先发出请求。这使得客户端和服务器能够实时交流和传输数据。

  • 数据帧:WebSocket 使用数据帧(Frame)来传输数据。数据帧是 WebSocket 数据的最小传输单元。数据帧可以被分割成多个片段来传输更大的数据。数据帧中包含了有效载荷(Payload)和一些控制信息,例如标识消息类型、是否为最后一个片段等。

  • 心跳检测:WebSocket 连接建立后,客户端和服务器可以通过发送心跳数据帧来维持连接。心跳检测可以确保连接的持久性,如果在一段时间内没有收到心跳响应,可以判断连接已断开。

3.WebSocket的报文结构

websocket的报文=结束标志位 + 操作码 + 帧长度 + 掩码

  • 第一位“FIN”:相当于 HTTP/2 里的“END_STREAM”,表示数据发送完毕。一个消息可以拆成多个帧,接收方看到“FIN”后,就可以把前面的帧拼起来,组成完整的消息。

  • “FIN”后面的三个位是保留位,目前没有任何意义,但必须是 0。

  • “Opcode”,操作码:其实就是帧类型,比如 1 表示帧内容是纯文本,2 表示帧内容是二进制数据,8 是关闭连接,9 和 10 分别是连接保活的 PING 和 PONG。

  • 掩码标志位“MASK”:表示帧内容是否使用异或操作(xor)做简单的加密。目前的 WebSocket 标准规定,客户端发送数据必须使用掩码,而服务器发送则必须不使用掩码。

  • “Payload len”:表示帧内容的长度。它是另一种变长编码,最少 7 位,最多是 7+64 位,也就是额外增加 8 个字节,所以一个 WebSocket 帧最大是 2^64。

  • “Masking-key”:掩码密钥,它是由上面的标志位“MASK”决定的,如果使用掩码就是 4 个字节的随机数,否则就不存在。

4.JAVA中的WebSocket

WebSocket作为一个HTML5标准,也就是前端提出的标准,后端服务器需要支持这种标准,也就是能准确解析WebSocket的数据包,按照约定的标准来办事儿,才能使用WebSocket。

Java 的 Servlet 3.1 规范中包含了对 WebSocket 的支持,也就是说支持Servlet 3.1的Web Server就支持WebSocket。实际开发中常用的tomcat、netty都支持websocket。

这里给出Spring Boot中使用WebSocket的demo。其底层是用的TomCat支持的WebSocket标准。

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

websocket处理器:

每一个 WebSocket 连接对应一个 WebSocketSession。当客户端通过 WebSocket 建立连接时,服务器会为每个连接创建一个 WebSocketSession 对象,用于表示该连接的会话信息。

处理器用于处理WebSocketSession。

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("WebSocket 连接建立:" + session.getId());
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        String payload = message.getPayload().toString();
        System.out.println("接收到消息:" + payload);
        session.sendMessage(new TextMessage("服务器收到消息:" + payload));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.err.println("WebSocket 传输错误:" + session.getId());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        System.out.println("WebSocket 连接关闭:" + session.getId());
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

配置、注册websocket处理器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private MyWebSocketHandler webSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/ws").setAllowedOrigins("*");
    }
}

前端代码示例:

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Test</title>
</head>
<body>
    <button onclick="sendMessage()">发送消息</button>
    <div id="messageBox"></div>

    <script>
        const ws = new WebSocket('ws://localhost:8080/ws');

        ws.onopen = function(event) {
            console.log('WebSocket 已连接');
        };

        ws.onmessage = function(event) {
            document.getElementById('messageBox').innerHTML += '<p>' + event.data + '</p>';
        };

        ws.onclose = function(event) {
            console.log('WebSocket 已关闭');
        };

        function sendMessage() {
            const message = prompt('请输入要发送的消息:');
            ws.send(message);
        }
    </script>
</body>
</html>

更多推荐

Pytest系列-数据驱动@pytest.mark.parametrize(7)

简介unittest和pytest参数化对比:pytest与unittest的一个重要区别就是参数化,unittest框架使用的第三方库ddt来参数化的而pytest框架:前置/后置处理函数fixture,它有个参数params专门与request结合使用来传递参数,也可以用parametrize结合request来传

【linux】paramiko介绍 + 路由器设置tc命令使用

背景:要给网络灵活的设置各种带宽限制,通过对路由器下发tc命令实现。设置python脚本的ssh链接+tc脚本下发+针对某一个id进行配置。Paramiko是一个用于在Python中进行SSH(SecureShell)协议通信的库。它提供了在远程服务器上执行命令、上传和下载文件、建立SSH连接等功能,使得开发者可以轻松

MySQL---优化&日志

目录一、MySQL优化3、mysqlserver上的优化3.1、MySQL查询缓存3.2、索引和数据缓存3.2、线程缓存二、MySQL日志2.1、redolog重做日志2.2、undolog回滚日志2.3、错误日志2.4、查询日志2.5、二进制日志2.5.1、基于binlog数据恢复实践操作六、慢查询日志一、MySQL

渗透测试信息收集方法和工具分享

文章目录一、域名收集1.OneForAll2.子域名挖掘机3.subdomainsBurte4.ssl证书查询二、获取真实ip1.17CE2.站长之家ping检测3.如何寻找真实IP4.纯真ip数据库工具5.c段,旁站查询三、端口扫描1.端口扫描站长工具2.masscan(全端口扫描)+nmap扫描3.scanport

科大讯飞分类算法挑战赛2023的一些经验总结

引言:ResNet是hekaiming大佬的早年神作,当年直接刷榜各大图像分类任务。ResNet是一种残差网络,咱们可以把它理解为一个子网络,这个子网络经过堆叠可以构成一个很深的网络,而ResNext在其基础上,进行了一定修改完善,通过引入Cardinatity后,模型性能得到了大幅度提升。(下图是经典ResNet残差

知识图谱实战应用28-基于py2neo的ICD-11疾病分类的知识图谱的查询与问答实战应用

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用28-基于py2neo的ICD-11疾病分类的知识图谱的查询与问答实战应用。使用基于py2neo的ICD-11疾病分类知识图谱,我们能够像探索一座生物医学宇宙般,穿梭在各种疾病之间。这个神奇的图谱可以帮助我们揭示各种疾病之间复杂而微妙的联系。就像一位专业的侦探,我

【大数据】Neo4j 图数据库使用详解

目录一、图数据库介绍1.1什么是图数据库1.2为什么需要图数据库1.3图数据库应用领域二、图数据库Neo4j简介2.1Neo4j特性2.2Neo4j优点三、Neo4j数据模型3.1图论基础3.2属性图模型3.3Neo4j的构建元素3.3.1节点3.3.2属性3.3.3关系3.3.4标签四、Neo4j搭建过程4.1搭建步

泰安ITSS认证流程,认证条件

ITSS认证流程,认证条件一、ITSS的意义ITSS认证——信息技术服务标准,是在工业和信息化部、国家标准化委的领导和支持下,由ITSS工作组研制的一套IT服务领域的标准库和一套提供IT服务的方法论。ITSS认证-信息技术服务标准是一套成体系和综合配套的信息技术服务标准库,全面规范了IT服务产品及其组成要素,用于指导实

结构型模式-享元模式

主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。我们将通过创建5个对象来画出20个分布于不同位置的圆来演示这种模式。由于只有5种可用的颜色,所以color属性

2-1 张量数据结构

张量概念张量是什么?单个元素叫标量(scalar),一个序列叫向量(vector),多个序列组成的平面叫矩阵(matrix),多个平面组成的立方体叫张量(tensor)。在深度学习中,标量、向量、矩阵、高维矩阵都统称为张量。在pytorch中,一个Tensor内部包含数据和导数两部分。Pytorch的基本数据结构是张量

Learn Prompt-角色扮演

模拟面试​当你在新闻中读到更多关于ChatGPT的内容时,你会听说ChatGPT可以代替医生、面试官、教师、律师等。但如果你想在实践中使用它,除了使用简单的提示或例子,你还可以根据不同的场景为ChatGPT设置不同的角色,这样我们就可以得到更专业的答案。让我们从一个简单的例子开始:首先我们可以让ChatGPT担任面试官

热文推荐