websocket详解

websocket作为一个可以在客户端和服务端进行全双工通信的协议,在即时通信,直播还有游戏运用十分广泛。但是本人在使用中时常搞不清他与http
和TCP
之间的联系,因此在这篇文章里将会介绍websocket的建立流程,与HTTP和TCP联系,websocket数据帧结构与解析
websocket的建立
websocket并不是像我们想象中类似tcp建立连接的,实际上websocket和http同属于网络模型中的应用层。
1 | 🔽 网络协议的分层结构(从下往上): |
而websocket也并不是独立于http的一个网络通信协议。
一句话总结websocket与http的关系:
WebSocket 是一种建立在 HTTP 协议上的网络通信协议,用来实现客户端和服务器之间的全双工通信。
更详细的解释
- WebSocket 的连接是 通过 HTTP 发起的,这个过程叫做“握手(handshake)”。
- 它使用 HTTP 协议的 GET 请求,带上一些特殊的头,比如:
1
2
3
4
5
6GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxxx==
Sec-WebSocket-Version: 13 - 服务端识别这是一个想要升级到 WebSocket 的请求,就会返回一个类似的响应:🔁 握手成功后,连接就从 HTTP 协议“升级”为 WebSocket 协议,此后就不是 HTTP 了。
1
2
3
4HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: yyyyyyy==
总结一下
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 | 0 1 2 3 |
字段说明:
字段 | 位数 | 含义 |
---|---|---|
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 | 可变长度 | 实际数据内容 |
为什么有这个帧结构?
- 保持轻量:相比 HTTP 的报文头,WebSocket 帧非常短小。
- 支持流式消息:消息可以分成多帧发(
FIN=0
),流式处理。 - 双向:客户端和服务器都能发帧,不需要谁等谁。
- 安全机制:客户端发来的消息必须加掩码(防止中间人攻击)。
举例说明:假设客户端通过 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 表示这是一个文本帧 |
✅ 确定这是一个完整的文本消息帧
第二个字节:85
(10000101
)
位 | 含义 |
---|---|
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 | // 原始数据(5字节) |
🟢 最终得到原始数据:"Hello"
三、流程总结(图示)
1 | 客户端调用:socket.send("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.