WebSocket原理详解(二)

WebSocket原理详解(一)-CSDN博客

目录

1.WebSocket协议的帧数据详解

1.1.帧结构

1.2.生成数据帧

2.WebSocket协议控制帧结构详解

2.1.关闭帧

2.2.ping帧

2.3.pong帧

3.WebSocket心跳机制


1.WebSocket协议的帧数据详解

1.1.帧结构

        WebSocket客户端与服务器通信的最小单位是帧(frame),一条完整消息是由一个或多个帧组成的。数据交互时,发送方会将消息切割为多个帧发送给接收方,接收方接收到消息帧后会重新组装为完整的消息。

        当WebSocket接收方接收到一个数据帧时会根据FIN(数据帧中的一个标识,用来判断当前帧是否当前消息的最后一帧)的值来判断是否已经接收到消息的最后一个数据帧。当接收到消息的最后一帧时,即可对消息进行处理。

WebSocket 帧由多个字段组成,各字段按顺序排列,这些字段共同构成了帧的头部和可选的负载数据。帧的基本结构如下:

字段长度(字节)描述
FIN1 位表示该帧是否为消息的最后一帧。1 表示是最后一帧,0 表示还有后续帧。
RSV1 - RSV3各 1 位保留位,默认值为 0。如果要使用,需要在握手阶段进行协商。
Opcode4 位定义帧的操作类型,如文本帧、二进制帧、关闭帧等。
Mask1 位表示负载数据是否经过掩码处理。客户端发送的帧必须进行掩码处理。
Payload length7 位、7 + 16 位或 7 + 64 位表示负载数据的长度。如果值在 0 - 125 之间,直接使用 7 位表示;如果是 126,则后续 2 个字节表示长度;如果是 127,则后续 8 个字节表示长度。
Masking - key0 或 4 字节如果 Mask 位为 1,则存在 4 字节的掩码密钥,用于对负载数据进行掩码处理。

各字段详细解释

1. FIN 字段

  • 作用:用来指示当前帧是否为一个消息的最后一帧。在发送大消息时,消息可能会被拆分成多个帧,FIN 位可以帮助接收方判断消息是否完整。
  • 取值
    • 1:表示这是消息的最后一帧。
    • 0:表示还有后续帧。

2. RSV1 - RSV3 字段

  • 作用:这三个保留位主要是为未来扩展 WebSocket 协议预留的。目前默认值都为 0,如果要使用这些位,需要在握手阶段进行协商。
  • 取值:通常为 0。

3. Opcode 字段

  • 作用:定义了帧的操作类型,接收方根据 Opcode 的值来决定如何处理接收到的帧。
  • 常见取值及含义
    • 0x0:表示延续帧,用于继续之前未完成的消息。
    • 0x1:表示文本帧,负载数据为 UTF - 8 编码的文本。
    • 0x2:表示二进制帧,负载数据为二进制数据。
    • 0x3-0x7:保留帧,留作未来非控制帧扩展使用
    • 0x8:表示关闭帧,用于关闭 WebSocket 连接。
    • 0x9:表示心跳检测的 ping 帧。
    • 0xA:表示心跳检测的 pong 帧,是对 ping 帧的响应。
    • 0xB-0xF:保留帧, 留作未来控制帧扩展使用

4. Mask 字段

  • 作用:指示负载数据是否经过掩码处理。为了提高安全性,客户端发送的帧必须将负载数据进行掩码处理,服务器发送的帧不需要进行掩码处理。
  • 取值
    • 1:表示负载数据经过掩码处理。
    • 0:表示负载数据未经过掩码处理。

5. Payload length 字段

  • 作用:表示负载数据的长度。由于负载数据长度可能不同,该字段采用了可变长度的编码方式。
  • 取值及编码方式
    • 如果值在 0 - 125 之间,直接使用 7 位表示负载数据的长度。
    • 如果值为 126,则后续 2 个字节(16 位)表示负载数据的长度。
    • 如果值为 127,则后续 8 个字节(64 位)表示负载数据的长度。

6. Masking - key 字段

  • 作用:如果 Mask 位为 1,该字段存在,且长度为 4 字节。它是一个随机生成的掩码密钥,用于对负载数据进行掩码处理和解掩码处理。
  • 掩码处理和解掩码处理:假设 P 是原始的负载数据,M 是掩码密钥,C 是经过掩码处理后的数据,则 C[i] = P[i] ^ M[i % 4],接收方可以使用相同的掩码密钥和异或运算对 C 进行解掩码得到 P

7. Payload data 字段

  • 作用:实际传输的数据,可以是文本、二进制数据等,具体取决于 Opcode 的值。

示例

假设客户端要发送一个简单的文本消息 "Hello",以下是一个简化的帧结构分析:

  • FIN:由于这是消息的唯一一帧,FIN 为 1。
  • RSV1 - RSV3:默认值为 0。
  • Opcode:消息是文本,Opcode 为 0x1。
  • Mask:客户端发送的帧需要掩码处理,Mask 为 1。
  • Payload length:消息长度为 5 字节,小于 126,直接用 7 位表示,值为 5。
  • Masking - key:随机生成 4 字节的掩码密钥,例如 0x12345678
  • Payload data:将 "Hello" 进行掩码处理后的数据。

1.2.生成数据帧

        从服务器发往客户端的数据也是同样的数据帧,但是从服务器发送到客户端的数据帧不需要掩码的。我们自己需要去生成数据帧,解析数据帧的时候我们需要分片。

消息分片:

        有时候数据需要分成多个数据包发送,需要使用到分片,也就是说多个数据帧来传输一个数据。比如将大数据分成多个数据包传输,分片的目的是允许发送未知长度的消息。这样做的好处是:

  • 大数据的传输可以分片传输,不用考虑到数据大小导致的长度标志位不够的情况
  • 和http的chunk一样,可以边生成数据边传递消息,可以提高传输效率。

        如果大数据不能被碎片化,那么发送端就必须将数据整个载入内存缓冲之中,然后进行计算长度等操作并发送。但是有了碎片化机制,服务器就可以选取适用的内存缓冲长度,然后当缓冲满了之后就发送一个消息碎片。

分片规则:

  • 如果一个消息不分片的话,那么该消息只有一帧(FIN为1,opcode非0);
  • 如果一个消息分片的话,它的构成是由
    • 1个起始帧:FIN为0,opcode非0
    • 然后若干帧:FIN为0,opcode为0
    • 1个结束帧:FIN为1,opcode为0

注意:

  • 当前已经定义的控制帧包括 0x8(close)、0x9(Ping)、0xA(Pong)。控制帧可以出现在分片消息中间,但是控制帧不允许分片,控制帧是通过它的opcode的最高有效位是1去确定的。
  • 组成消息的所有帧都是相同的数据类型,在第一帧中的opcode中指明。组成消息的碎片类型必须是文本,二进制,或者其他的保留类型。

2.WebSocket协议控制帧结构详解

        目前,控制帧的操作码定义了0x08(关闭帧)、0x09(Ping帧)、0x0A(Pong帧)。0x0B-0x0F是为那些将来可能定义而目前尚未定义的控制帧预留的。

        控制帧用于WebSocket协议交换状态信息,控制帧可以插在消息片段之间。

注意:所有的控制帧的负载长度务必不大于125字节,并且禁止对控制帧进行分片处理。

2.1.关闭帧

        关闭帧的操作码Opcode是0x08。

        关闭帧可能包含数据部分(应用数据帧),该部分表明了关闭的原因,例如:端点关闭、端点接收帧过大或端点收到的帧不符合预期。

  • 如果有数据部分,则数据的前两个字节必须是一个无符号整数(网络字节序),该无符号整数表示了一个状态码,具体定义哪些关闭码将在后面的文章中介绍。
  • 在无符号整数后面,可能还有一个UTF-8编码的数据,表示关闭原因,关闭原因由开发者自行定义(可选),并无规范。关闭原因并不一定是对人可读的,但会对调试或传递相关信息起到一定的作用。由于数据不能保证人类可读,所以客户端一定不能将其显示给用户(会在关闭事件onclose中)

        客户端发送给服务器的关闭帧必须掩码处理。应用程序在发送了一个关闭帧后,禁止再发送任何数据(此时处于CLOSING状态)。

        如果端点(客户端或服务器)收到了一个关闭帧,并且之前没有发送过关闭帧,则端点必须发送一个关闭帧作为响应。(当端点发送一个关闭帧回应时,通常会显示它收到的状态码)。当端点可以发送关闭响应时应尽快发送关闭响应。一个端点可以延迟发送响应直到它的当前消息发送完毕(例如,已经发送了大多数的消息片段,则端点可能会在发送关闭响应帧前先将剩下的消息帧发送出去)。但不能保证对方在已经发送了关闭帧后还能够继续处理这些数据。

        在双方都已发送并接收了关闭帧后,端点需要断掉WebSocket连接并且必须关闭底层的TCP连接。服务器必须立即切断底层TCP连接,客户端最好等待服务器断开连接,但也可以在发送并接收了关闭帧后任何时候断开连接,例如在一段时间内服务器仍没有断开TCP连接。

        如果服务器和客户端同时发送了关闭帧,两端都会接收关闭帧,并且都需要断开TCP连接。

关闭帧示例

假设客户端要发送一个关闭帧,状态码为 1000(正常关闭),没有额外的关闭原因。其帧结构如下:

  • FIN:1
  • RSV1 - RSV3:0 0 0
  • Opcode:0x8
  • Mask:1(客户端发送)
  • Payload length:2(状态码占 2 字节)
  • Masking - key:随机生成的 4 字节密钥
  • Payload data:状态码 1000(2 字节)

2.2.ping帧

        Ping帧的操作码为0x09。

        Ping帧可以包含应用数据。

        一旦接到了一个Ping帧,端点必须返回一个Pong帧作为响应,除非它收到了一个关闭帧。它应在可以发送时尽快发送Pong帧响应。

        端点可以在连接建立后一直到连接关闭前任何时候发送Ping帧。

ping 帧示例

客户端发送一个 ping 帧,包含自定义的心跳信息 "HEARTBEAT"。其帧结构如下:

  • FIN:1
  • RSV1 - RSV3:0 0 0
  • Opcode:0x9
  • Mask:1(客户端发送)
  • Payload length:9("HEARTBEAT" 长度为 9)
  • Masking - key:随机生成的 4 字节密钥
  • Payload data:经过掩码处理的 "HEARTBEAT"

2.3.pong帧

        Pong帧的操作码为0x0A。

        Pong帧必须与Ping帧拥有相同的应用数据部分。

        如果端点收到了多个Ping帧,但还没来的及全部回应,可以只回应最后一个Ping帧。

        Pong帧可以在未收到Ping帧时就被发送,用作单向心跳包。

        对未被请求的Pong帧(对方主动发送的Pong帧)进行回应是不需要的。

3.WebSocket心跳机制

        心跳机制的核心是客户端和服务器之间定期发送和接收特定的心跳消息。一般而言,客户端和服务器中的一方会定期发送心跳消息(如 ping 帧),另一方收到后则立即回复相应的响应消息(如 pong 帧)。若在规定时间内未收到响应,就判定连接出现问题,进而进行重连或其他处理。

        WebSocket 协议本身定义了 ping 帧和 pong 帧用于心跳检测。

  • ping 帧:由客户端或服务器发送,用于主动检测对方的连接状态。
  • pong 帧:是对 ping 帧的响应,当一方收到 ping 帧后,必须尽快发送一个 pong 帧,且 pong 帧的负载数据应与 ping 帧的负载数据相同。

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

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

相关文章

MySQL 进阶 面经级

会用数据库,找大厂工作是远远不够的。 本人2025美团暑期AI面试好几个MySQL场景问题不会答,已脏面评。遂在此整理学习! 文章目录 美团AI面1.数据库分片sharding的概念,它有什么优势和挑战?优势Sharding 挑战 2. 分库分表的常见策…

基于单片机的智能奶茶机(论文 +源码)

1总体架构设计 本课题为基于单片机的智能奶茶机设计,其系统架构上设计如图2.1所示,整个系统包括了DS18B20温度传感器、继电器模块、LCD液晶、蜂鸣器、按键、STC89C52单片机等器件,在功能上用户可以通过按键键控制选择甜度和添加物以及设置温度…

Hue:一个大数据查询工具

Hue(Hadoop User Experience)是一个用于大数据平台、数据库以及数据仓库查询的开源工具,旨在通过 Web 界面简化用户与 Hadoop 生态系统以及各种数据存储的交互。 Hue 支持的数据源包括数据库(Apache Hive、Apache Impala、MySQL、…

并发多线程八股

并发多线程 1.Java里面的线程和操作系统的线程一样吗?2.Java的线程安全在三个方面体现:3.保证数据一致性的方案4.线程创建的方式1)Thread类2)Runnable接口3)Callable接口和FutureTask4)线程池(e…

VBA数据库解决方案第二十讲:SQL在VBA中几种常见的表达方式

《VBA数据库解决方案》教程(版权10090845)是我推出的第二套教程,目前已经是第二版修订了。这套教程定位于中级,是学完字典后的另一个专题讲解。数据库是数据处理的利器,教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

大语言模型智体的综述:方法论、应用和挑战(下)

25年3月来自北京大学、UIC、广东大亚湾大学、中科院计算机网络信息中心、新加坡南阳理工、UCLA、西雅图华盛顿大学、北京外经贸大学、乔治亚理工和腾讯优图的论文“Large Language Model Agent: A Survey on Methodology, Applications and Challenges”。 智体时代已经到来&a…

《STL 六大组件之容器篇:简单了解 list》

目录 一、list 简介二、list 的常用接口1. 构造函数(constructor )2. 迭代器(iterator)3. 容量、修改和访问(capacity 、modify and access) 一、list 简介 简单来说,list 就是数据结构初阶中学…

nmslib 是一个 超快、适用于高维向量的最近邻搜索库,基于 HNSW 算法,被广泛用于 语义搜索、推荐系统、人脸识别等任务

nmslib 是什么? nmslib(Non-Metric Space Library)是一个 高效的最近邻搜索(ANN, Approximate Nearest Neighbor Search) 库,专门用于 高维向量搜索,适用于 文本、图像、语音等嵌入向量 的相似…

前端流式输出实现详解:从原理到实践

前端流式输出实现详解:从原理到实践 前言一、流式输出核心原理1.1 什么是流式输出?1.2 技术优势对比1.3 关键技术支撑 二、原生JavaScript实现方案2.1 使用Fetch API流式处理关键点解析: 2.2 处理SSE(Server-Sent Events&#xff…

【STM32】最后一刷-江科大Flash闪存-学习笔记

FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程,(系统存储器用于存储原厂写入的BootLoader程序,用于串口…

梯度(Gradient)与步长(Step Size)

梯度(Gradient)与步长(Step Size) 梯度与步长是优化算法(如梯度下降法)的核心概念。以下是它们的详细解释: 梯度(Gradient) ​定义 梯度是一个向量,表示多元…

freecad二开 xmlrpc接口api qtgui

FreeCAD.ConfigGet("UserAppData") 文件夹下创建mod文件夹 mod文件夹底下创建插件文件夹my_server: freecad_server.py: from xmlrpc.server import SimpleXMLRPCServer import FreeCADGui import FreeCADimport queue from PySide2.QtCore import QTi…

鸿蒙NEXT开发日志工具类(ArkTs)

import hilog from ohos.hilog; import { JSON } from kit.ArkTS; import { BusinessError } from kit.BasicServicesKit; import { StrUtil } from ./StrUtil;/*** 日志工具类* author: 鸿蒙布道师* since: 2024/03/31*/ export class LogUtil {private static logSize: numbe…

《Linux运维总结:基于银河麒麟V10+ARM64架构CPU源码编译部署单实例redis7.2.6》

总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:《Linux运维篇:Linux系统运维指南》 一、环境信息 环境信息如下: 主机IP 操作系统 Redis版本 CPU架构 192.168.1.111 K…

基于LSTM的文本分类1——模型搭建

源码 # coding: UTF-8 import torch import torch.nn as nn import torch.nn.functional as F import numpy as npclass Config(object):"""配置参数类,用于存储模型和训练的超参数"""def __init__(self, dataset, embedding):self.…

小了 60,500 倍,但更强;AI 的“深度诅咒”

作者:Ignacio de Gregorio 图片来自 Unsplash 的 Bahnijit Barman 几周前,我们看到 Anthropic 尝试训练 Claude 去通关宝可梦。模型是有点进展,但离真正通关还差得远。 但现在,一个独立的小团队用一个只有一千万参数的模型通关了…

nextjs使用02

并行路由 同一个页面,放多个路由,, 目录前面加,layout中可以当作插槽引入 import React from "react";function layout({children,notifications,user}:{children:React.ReactNode,notifications:React.ReactNode,user:React.Re…

github 无法在shell里链接

当我在shell端git push时,我发现总是22 timeout的问题。 我就进行了以下步骤的尝试并最终得到了解决。 第一步,我先确定我可以curl github,也就是我网络没问题 curl -v https://github.com 如果这个时候不超时和报错,说明网络…

当前主流的大模型知识库软件对比分析

以下是当前主流的大模型知识库软件对比分析,涵盖功能特性、适用场景及优劣势,结合最新技术动态和行业实践提供深度选型参考: 一、企业级智能知识库平台 1. 阿里云百炼(Model Studio) 核心能力:基于RAG技…

Java的比较器 Comparable 和 Comparator

在 Java 中,Comparable 和 Comparator 是用于对象排序的重要接口。它们提供了不同的排序方式,适用于不同的需求,同时在 Java 底层排序算法中发挥着关键作用。本文将从基础概念、使用方法、排序实现(包括升序、降序)、底…