websocket详解

tianyi Lv2

websocket作为一个可以在客户端和服务端进行全双工通信的协议,在即时通信,直播还有游戏运用十分广泛。但是本人在使用中时常搞不清他与httpTCP之间的联系,因此在这篇文章里将会介绍websocket的建立流程,与HTTP和TCP联系websocket数据帧结构与解析

websocket的建立

websocket并不是像我们想象中类似tcp建立连接的,实际上websocket和http同属于网络模型中的应用层。

1
2
3
4
5
6
7
🔽 网络协议的分层结构(从下往上):
-------------------------------------------------
应用层:HTTP、WebSocket、FTP、DNS(你写的业务逻辑就在这里)
传输层:TCP / UDP(处理连接、可靠性、分片)
网络层:IP(处理路由、IP 地址)
链路层:以太网(MAC 地址、局域网通信)
-------------------------------------------------

而websocket也并不是独立于http的一个网络通信协议。

一句话总结websocket与http的关系:
WebSocket 是一种建立在 HTTP 协议上的网络通信协议,用来实现客户端和服务器之间的全双工通信。

更详细的解释

  • WebSocket 的连接是 通过 HTTP 发起的,这个过程叫做“握手(handshake)”。
  • 它使用 HTTP 协议的 GET 请求,带上一些特殊的头,比如:
    1
    2
    3
    4
    5
    6
    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: xxxxxx==
    Sec-WebSocket-Version: 13
  • 服务端识别这是一个想要升级到 WebSocket 的请求,就会返回一个类似的响应:
    1
    2
    3
    4
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: yyyyyyy==
    🔁 握手成功后,连接就从 HTTP 协议“升级”为 WebSocket 协议,此后就不是 HTTP 了。

总结一下

WebSocket 是建立在 HTTP 上的一种新协议,只用 HTTP 来完成连接建立(握手),之后就切换成 WebSocket,进行全双工通信

所以它俩既有关联(在握手时),也有本质区别(通信机制不同)。

websocket与TCP的联系

刚刚讲了websocket是通过HTTP来建立连接的,但是这并不是说明websocket是基于HTTP的协议,相反WebSocket 是基于 TCP 的一种应用层协议

补充说明

WebSocket 不是在 HTTP 之上做“封装”的协议,而是:

  • 用 HTTP 做握手升级

  • 握手完成后,脱离 HTTP,变成基于 TCP 的双向通信通道

  • WebSocket 有自己独立的 帧格式(frame format),不是 HTTP 的数据格式。

用一个类比来说明:假如说websocket是一条高速公路,那么HTTP就是高速公路的收费站,它帮你打开 WebSocket 的门(通过握手);而TCP则是高速公路的路

websocket的帧结构

首先我们来说说这个帧是什么。
websocket要想发送数据就得把数据(文本、二进制)打包成帧格式进行发送,WebSocket 使用的是自定义的帧(frame)结构

websocket数据帧结构

我们来看看一个 WebSocket 帧的结构(简化版):

1
2
0               1               2               3  
| FIN | RSV | OPCODE | MASK | PAYLOAD LEN | ... |

字段说明:

字段 位数 含义
FIN 1 bit 是否是消息的最后一帧(1=是)
RSV1~RSV3 3 bits 保留位,扩展用(一般为0)
Opcode 4 bits 帧类型:文本(0x1)、二进制(0x2)、关闭(0x8)等
MASK 1 bit 客户端 -> 服务端 的消息必须设置为 1,表示有掩码
Payload length 7/16/64 有效负载的长度(数据长度)
Masking-key 0或4字节 掩码(客户端发的数据都要用这个加密)
Payload data 可变长度 实际数据内容

为什么有这个帧结构?

  1. 保持轻量:相比 HTTP 的报文头,WebSocket 帧非常短小。
  2. 支持流式消息:消息可以分成多帧发(FIN=0),流式处理。
  3. 双向:客户端和服务器都能发帧,不需要谁等谁。
  4. 安全机制:客户端发来的消息必须加掩码(防止中间人攻击)。

举例说明:假设客户端通过 WebSocket 发送了一条文本消息 “hello”

好,那我们就一起来手动解析一个真实的 WebSocket 数据帧,看看它是如何在 TCP 上封装并传输的!

我们来模拟一个真实的数据帧(二进制)是怎样的:

1
81 85 37 fa 21 3d 7f 9f 4d 51 58

这个就是你在抓包(用 Wireshark)或者调试工具里能看到的十六进制数据。我们一字节一字节来拆解它:


✅ 一、逐字节拆解

1
[81] [85] [37 fa 21 3d] [7f 9f 4d 51 58]

第一个字节:81(二进制是 10000001

含义
1 FIN = 1,表示这是消息的最后一帧(也是唯一一帧)
000 RSV1, RSV2, RSV3 = 0,保留位
0001 Opcode = 0x1 表示这是一个文本帧

确定这是一个完整的文本消息帧


第二个字节:8510000101

含义
1 MASK = 1,表示客户端发送的数据进行了掩码处理(必须)
0000101 Payload length = 5,表示实际内容是 5 个字节("hello" 的长度)

内容长度为 5,带掩码


第 3-6 字节:掩码 Masking Key

1
37 fa 21 3d

这是 WebSocket 客户端发送数据时,必须加的掩码。后面的数据需要“解掩码”才能变回原始内容。


第 7-11 字节:Payload(被掩码过的)

1
7f 9f 4d 51 58

这不是 "hello" 的 ASCII,而是加密过的数据,解码方式如下 👇


二、解掩码操作(恢复原文)

WebSocket 的掩码算法很简单:

每个 payload 字节,和 masking-key 的每一字节轮流进行异或(XOR)

1
2
3
4
5
6
7
8
9
10
11
12
// 原始数据(5字节)
masked = [0x7f, 0x9f, 0x4d, 0x51, 0x58]
mask = [0x37, 0xfa, 0x21, 0x3d]

// 解掩码操作:masked[i] ^ mask[i % 4]
decoded = [
0x7f ^ 0x37, // 0x48 => 'H'
0x9f ^ 0xfa, // 0x65 => 'e'
0x4d ^ 0x21, // 0x6c => 'l'
0x51 ^ 0x3d, // 0x6c => 'l'
0x58 ^ 0x37, // 0x6f => 'o'
]

🟢 最终得到原始数据:"Hello"


三、流程总结(图示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
客户端调用:socket.send("Hello")

⬇️

WebSocket 协议封装:
┌────────────┬──────────┬───────────────┐
│ 帧头部(2B) │ 掩码(4B) │ 加密数据(5B) │
└────────────┴──────────┴───────────────┘

⬇️

通过 TCP 发送出一个字节流:

81 85 37 fa 21 3d 7f 9f 4d 51 58

⬇️

服务器收到后解掩码,拿到真实数据:Hello

结论:WebSocket 帧结构是“TCP上传输协议的协议”

它规定了:

  • 数据从哪里开始(帧头)
  • 是文本还是二进制
  • 是否掩码
  • 数据长度
  • 实际数据内容(可拆分)
  • Title: websocket详解
  • Author: tianyi
  • Created at : 2025-04-13 11:10:54
  • Updated at : 2025-04-13 14:02:32
  • Link: https://github.com/ztygod/2025/04/13/websocket详解/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments