一、什么是 WebSocket 服务?
WebSocket 服务指的是一种能够遵从 WebSocket 协议,并支持 WebSocket 握手和数据收发的服务器程序。
其意义与“HTTP 服务”“SMTP 服务”等概念相同,都属于应用层协议服务器。
理解 WebSocket 协议之前,首先要理解“协议”本身。
二、什么是协议?为什么需要协议?
协议(Protocol)是通信双方必须共同遵守的数据格式和交互规则。
无论数据以光信号、电信号还是其他方式传输,最终到达计算机的都是字节流。如果没有协议,接收端无法知道:
- 哪些字节代表消息的开始
- 哪些字节表示长度
- 哪些是正文
- 哪些表示消息已经结束
这就是网络协议的作用——为这些问题提供统一的答案。
TCP/UDP 解决的是“如何传输字节”的问题,而应用层协议(如 HTTP、WebSocket、自定义协议)解决的是:
这些字节如何被解释为完整的业务消息。
三、应用层协议必须解决两个核心问题
因为 TCP 是一个面向字节流的协议,没有消息边界,所以应用层必须解决两个问题:
1. 如何序列化与反序列化?
因为网络只能传输二进制字节,业务数据必须序列化:
- JSON / XML(文本协议)
- Protobuf / Thrift(高效二进制协议)
- 或通信双方定义的自定义二进制结构
如果双方序列化方式不一致,通信必然失败。
2. 如何解决拆包 / 粘包问题?
TCP 传输过程中,可能出现:
- 拆包:一条消息被分成多个 TCP 包
- 粘包:多条消息合并成一个 TCP 包
- 半包:消息只到了一半
因此协议必须定义消息边界的方式,例如:
- 固定分隔符(如 HTTP 头部的
\r\n\r\n) - 长度字段(常见于二进制协议,如 WebSocket、Protobuf)
- 固定长度
- Chunked 分块(HTTP/1.1)
没有这些规则,就根本无法从 TCP 字节流中恢复业务消息。
四、WebSocket 协议如何解决拆包与序列化?
WebSocket 在 RFC6455 中定义了清晰且固定的帧格式(Frame) ,其中包含:
- FIN、RSV 信息
- opcode(文本帧、二进制帧、ping 等)
- mask 标记
- payload length(可为 7/16/64 位)
- masking key(客户端 → 服务端必有)
- payload 数据内容
这使得 WebSocket 可以:
1. 用 payload length 精准确定消息边界 → 解决拆包问题
不依赖特殊字符,也不是固定长度,而是明确的:
帧头确定数据长度 → 根据长度读取完整 payload
完全解决粘包/拆包。
2. 序列化方式自由决定 → 解决反序列化问题
WebSocket 不规定帧内数据的格式:
- 文本帧通常 UTF-8 文本
- 二进制帧可使用 JSON、Protobuf、自定义结构等
五、为什么可以自定义协议?
通用协议(HTTP、WebSocket)由标准组织制定,被浏览器、nginx、Tomcat 等广泛支持。
但我们也可以自己设计协议:
- 定义协议头(例如前 4 字节表示长度)
- 定义序列化格式
- 定义校验、版本等字段
- 双方按统一规则拆包和反序列化
只要双方遵从同一协议即可正常通信。
但必须注意:
浏览器、标准服务器不会支持你的协议,一切拆包、反序列化、加密等逻辑必须自行实现。
✅ 三、总结
- 协议是解释字节流的规则。
- TCP 是无边界的字节流,所以应用层协议必须定义序列化方式和消息边界规则。
- WebSocket 通过固定的帧结构(payload length + masking key)解决拆包问题,通过文本/二进制帧解决序列化问题。
- 自定义协议完全可行,但拆包、安全、序列化完全由你负责。