10 WebSocket
约 4431 字大约 15 分钟
2025-06-18
WebSocket是一种全双工通信协议,允许客户端和服务器之间建立特久化的双向通信连接。ebSocket 协议设计的初衷是解决 HTTP 协议在实时交互上的局限性,例如长轮询、Ajax 等方法的高延迟问题。WebSocket 可以在单个 TCP 连接上实现客户端与服务器之间的实时、低延迟的数据传输。
- 单工:单向通信,无法反馈。
- 半双工:双向通信,但不能同时进行。
- 双工:双向通信,可以同时进行。
Http的局限性
HTTP(超文本传输协议)是一种用于传输数据的协议,但它也有一些局限性,主要包括以下几点:
无状态性 HTTP 是一种无状态协议,每个请求都是独立的,不会保留客户端的上下文信息。这使得实现用户会话和状态管理变得复杂,需要额外的机制(如 cookies 或 sessions)。
性能问题 延迟:每次请求都需要重新建立连接,尤其是在使用 HTTP/1.1 时,存在多个请求时会造成额外的延迟。 带宽浪费:每次请求都需要发送请求头,增加了网络带宽的消耗。
安全性 加密不足:传统的 HTTP 传输是明文的,数据在传输过程中容易被窃听和篡改。虽然可以通过 HTTPS(HTTP Secure)来增强安全性,但 HTTP 本身并不提供加密。
不支持双向通信 HTTP 是请求-响应模型,客户端发起请求后,服务器才能响应,不支持实时双向通信。这在需要即时数据传输的应用中(如聊天应用)造成了限制。
请求重定向和缓存管理的复杂性 在复杂的应用中,HTTP 请求的重定向和缓存管理可能导致性能下降和数据一致性问题,增加了开发和维护的复杂性。
适应性差 HTTP 的设计初衷是用于静态网页的传输,对于现代动态内容和实时应用(如流媒体、在线游戏等)支持不够灵活。
虽然 HTTP 是互联网通信的基础,但其局限性促使开发者寻求更高效、更安全的替代方案,如 WebSocket、HTTP/2 和 HTTP/3 等。理解这些局限性有助于在设计系统时做出更明智的选择。
WebSocket 协议
WebSocket 是基于 TCP 的协议,定义在 IETF 的 RFC 6455 标准中后由 RFC 7936 补充规范。。WebSocket 的连接从一个标准的 HTTP 请求开始,经过一次协议升级后,建立起一个全双工的 WebSocket 连接。
WebSocket 默认使用以下两个端口:
80 端口:非加密的 WebSocket 协议,使用 ws:// 开头的 URL。 443 端口:加密的 WebSocket 协议(通过 TLS/SSL 加密),使用 wss:// 开头的 URL。 WebSocket 的 URL 格式与 HTTP 类似,但有一些特定的细节。以下是 WebSocket URL 的基本结构:
ws://[host]:[port]/[path]
WebSocket 的 URL 一共由四部分构成:
协议:
ws://
:表示 WebSocket 协议。wss://
:表示 WebSocket Secure(加密的 WebSocket),类似于 HTTPS。主机(host): 服务器的域名或 IP 地址,例如 example.com 或 192.168.1.100。
端口(port)(可选): WebSocket 默认端口是 80(ws)和 443(wss),可以省略。如果使用其他端口,则需要显式指定,例如 :8080。
路径(path): 服务器上用于 WebSocket 连接的具体路径,例如 /socket。
示例
// 非加密的 WebSocket 连接
ws://example.com/socket
// 加密的 WebSocket 连接
wss://example.com/socket
// 指定端口的连接
ws://example.com:8080/socket
// 带路径的连接
wss://example.com/api/v1/chat
WebSocket URL 的格式与 HTTP URL 类似,主要区别在于使用的协议前缀(ws 或 wss)。使用时要确保服务器能够处理 WebSocket 连接,并在前端正确配置 URL。
工作原理
WebSocket 连接从 HTTP 请求开始,客户端通过 HTTP 升级机制请求升级协议:
- 客户端发起 HTTP 请求,并在请求头中包含特定的 WebSocket 字段以表示希望建立 WebSocket 连接。
- Upgrade: websocket:表示希望将协议升级为 WebSocket。
- Connection: Upgrade:告知服务器此次连接希望协议升级。
- Sec-WebSocket-Key:客户端生成的随机密钥,用于服务器生成握手应答的 Sec-WebSocket-Accept。
- Sec-WebSocket-Version:指定 WebSocket 的版本,当前标准版本是 13。
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- 服务器收到请求后,返回 101 状态码,并附带确认的握手信息。
- HTTP/1.1 101 Switching Protocols :状态行,它由三个部分组成: 协议版本:HTTP/1.1 表示使用的 HTTP 版本。 状态码:101 是状态码,表示服务器已接受客户端的请求并正在切换协议。 状态描述:Switching Protocols 是对状态码的描述,说明了服务器的响应内容。
- Sec-WebSocket-Accept:由 Sec-WebSocket-Key 和特定算法生成的哈希值,用于确认握手的安全性。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
数据帧传输
WebSocket 的数据通过帧 (frame) 进行传输("帧"是指在客户端和服务器之间传输的数据单元)。每一帧都有固定的格式,包含数据以及控制信息。WebSocket 支持以下几种帧类型:
- 文本帧(Text Frame):用于传输文本数据,通常是 UTF-8 编码的字符串。
- 二进制帧(Binary Frame):用于传输二进制数据,如图片、音视频数据等。
- 关闭帧(Close Frame):用于关闭连接。
- Ping 和 Pong 帧:用于检测连接的活跃性,Ping 由客户端或服务器发送,Pong 由对方响应。
帧格式
- FIN: (1bit),表示是否为最后一帧。如果为1,表示这是最后一帧;如果为0,表示后面还有帧。
- RSV1, RSV2, RSV3:(各1bit)保留位,用于扩展协议。通常在初始实现中,这些位应设置为0。
- Opcode: (4bit),用于表示帧的类型。 0x0:继续帧 0x1:文本帧 0x2:二进制帧 0x3~0x7:预留给以后的非控制帧 0x8:连接关闭帧 0x9:Ping 帧 0xA:Pong 帧 0xB~0xF:预留给以后的控制帧
- Mask: (1bit),表示是否对数据进行掩码处理。 客户端到服务器的消息必须使用掩码 服务器到客户端的消息不需要使用掩码
- Payload length: (7bit/7+16bit/7+64bit),表示数据的长度。 当它的值在 0 到 125 之间时,表示有效负载的字节数。 当它的值是 126(0x7E)时,表示后面将有 2 字节(16 位)来表示有效负载的真实长度(有效负载长度在 0 到 65535 之间)。 当它的值是 127(0x7F)时,则表示后面会有 8 字节(64 位)来表示有效负载的真实长度。
- Masking Key:(0或4bytes) 所有从客户端发往服务端的数据帧都已经与一个包含在这一帧中的32 bit的掩码进行了运算。 如果mask标志位(1 bit)为1,那么这个字段存在,如果标志位为0,那么这个字段不存在。
- Payload data:实际传输的数据,根据Payload length字段的值来确定长度。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
保持连接与心跳
WebSocket 在建立连接后会保持连接状态,直到一方显式关闭连接。为了防止连接由于网络问题或闲置超时而断开,WebSocket 支持通过 Ping-Pong 机制 来检测连接的活跃性。客户端或服务器可以发送一个 Ping 帧,接收方必须在合理时间内响应一个 Pong 帧,确保连接仍然正常。
Ping-Pong 机制的示例流程:
- 客户端发送 Ping 帧: 客户端发送一个 Ping 帧,Payload Data 可能包含当前时间戳。 帧格式:| FIN | RSV | Opcode: 0x9 | MASK=1 | Payload Length | Masking key=6 | Payload Data=时间戳 |
- 服务器接收并响应 Pong 帧: 服务器接收到 Ping 帧后,立即发送一个 Pong 帧,Payload Data 与接收到的 Ping 帧的 Payload Data 相同。 帧格式:| FIN | RSV | Opcode: 0xA | MASK=0 | Payload Length | Payload Data=时间戳 |
- 客户端接收 Pong 帧: 客户端接收到 Pong 帧后,可以确认连接仍然活跃。 客户端可以根据时间戳计算往返时间(RTT),以评估网络延迟。
在使用 WebSocket 协议的 Ping-Pong 机制的时候,有以下事项需要再次强调:
双向检测: 客户端和服务器都可以发送 Ping 帧,并期望对方发送 Pong 帧。这使得双方都能检测到连接的活跃性。
超时处理: 如果发送 Ping 帧的一方在一定时间内没有收到 Pong 帧,可以认为连接已经断开或不活跃,并采取相应的措施(如关闭连接或重新连接)。
Payload Data: Ping 和 Pong 帧的 Payload Data 是可选的,但通常会包含一些简单的数据(如时间戳),以便双方能够进行更详细的检测和分析。
关闭连接
WebSocket 连接关闭时,双方需要发送一个关闭帧(Close Frame)。关闭帧包含一个关闭码和关闭原因,可以用于表明连接关闭的原因。具体来说,关闭帧的格式遵循 WebSocket 协议规范,其中状态码的位置和结构如下:
- FIN 位: 1 bit
- RSV 位: 3 bits (保留位,一般用于扩展)
- Opcode: 4 bits (对于关闭帧,值为 0x8)
- MASK: 1 bit (指示消息是否被掩码,客户端消息必须掩码)
- Payload length: 7 bits, 7+16 bits, 或 7+64 bits (指示随后的有效负载的长度)
- Masking key: 0或4 bytes (如果 MASK 位为 1,则使用,包含用于掩码的密钥)
- Payload data: (长度通过 Payload length 指定) 关闭状态码: 在关闭帧的有效负载部分的最开始,紧接在 Payload length 字段后面。这两个字节 (未经掩码处理)表示状态码(Close Code)。 关闭原因: 状态码之后可以跟随一个可选的 UTF-8 字符串,描述关闭的原因。
常见的标准关闭码有:
- 1000:正常关闭。
- 1001:服务器或客户端因特殊原因需要关闭(如服务器关闭)。
- 1002:协议错误。
- 1003:接收到不支持的数据类型。
- 1006:异常关闭,没有收到关闭帧。
- 1007:收到包含不一致数据的帧,导致连接关闭。例如,格式不正确的内容。
- 1009:发送的消息超过了可以接收的大小。
- 1011:服务器遇到错误,无法完成请求。
- 1015:当 WebSocket 连接在 TLS/SSL 握手期间失败时,使用此状态码。 除了上述标准状态码外,也可以定义自定义的关闭状态码,通常建议使用在 1,000 到 1,999 之间的代码(一定要注意避免与标准代码冲突)。
示例关闭帧字节流:
| FIN | RSV | Opcode | MASK | Payload length | Close Code (2 bytes) | Close Reason (n bytes) |
关闭流程非常简单就是发送和接收数据,所以对于应用层的 WebSocket 而言就只有两步:
- 发送: 当一方要关闭连接时,它会构造关闭帧并在 Payload 中写入状态码和可能的关闭原因,然后将其发送给另一方。
- 接收: 接收方在接收到关闭帧后,会读取 Payload,首先获取状态码,然后读取可选的关闭原因,从而知道连接关闭的具体情况。
如果是基于 TCP 的四次挥手进行描述就是以下四步:
发起关闭请求: 一方调用关闭操作: 连接的一方(可以是客户端或服务器)决定关闭连接时,调用相应的 close() 方法。 发送关闭帧: 主动断开连接的一方会创建并发送一个关闭帧(Close Frame),该帧包含关闭状态码和可选的关闭原因。
接收关闭帧: 接收帧: 被动断开连接的一方(接收方)收到关闭帧时,会首先解析该帧的状态码和可选原因。 处理关闭帧 接收方可以根据关闭状态码进行相应的处理(例如,记录日志,更新连接状态等)。
发送响应的关闭帧 (Pong): 回应关闭请求: 接收方在确认关闭请求后(可能进行清理操作)会发送自己的关闭帧,这通常是一个回应帧。 格式: 该响应关闭帧也会包含一个状态码,通常是 1000(正常关闭),并可能附带关闭原因。
最终关闭连接: 双方完成关闭: 一旦双方都发送并接收了关闭帧,WebSocket 连接将被正式关闭。 清理资源: 关闭连接时,双方可以释放相关资源,如定时器、事件监听器等。
安全性
WebSocket 使用 wss(WebSocket Secure)协议进行安全通信的方式与使用 ws(WebSocket)协议类似,但需要额外的安全配置。
- WSS: 是通过 TLS/SSL 加密的 WebSocket 协议,类似于 HTTPS 相对于 HTTP 的形式。使用 WSS 可以确保数据在传输过程中得到加密,避免被窃听和篡改。
- 端口: WSS 通常使用 443 端口,而 WS(不安全)使用 80 端口。
在使用 WSS 时,必须提供有效的 SSL/TLS 证书。可以使用:
- 自签名证书: 用于开发和测试,但浏览器可能会显示安全警告。
- 公认的证书: 从受信任的证书颁发机构获取的证书,用于生产环境,以确保通信的安全性。
webSocket和Http对比
相同点和不同点
相同点
- 应用层协议: WebSocket 和 HTTP 都属于应用层协议,主要用于数据的传输。
- 基于 TCP: 两者都依赖于 TCP 协议进行底层的数据传输,确保数据的可靠性和顺序。
- 跨平台兼容性: WebSocket 和 HTTP 都可以在不同的平台和设备上使用,支持跨浏览器和跨设备的通信。
- 使用标准的 URL: WebSocket 和 HTTP 都使用类似的 URL 结构,例如
http://
和ws://
不同点
特性 | HTTP | WebSocket |
---|---|---|
连接模式 | 短连接 | 长连接 |
通信方式 | 单向(请求-响应) | 双向(全双工通信) |
连接建立 | 每次请求建立新连接,之后关闭连接 | 初始通过 HTTP 握手后建立持久连接 |
延迟 | 每次请求都需要重新建立连接,延迟较高 | 连接一旦建立后,延迟较低,可实时传输数据 |
数据格式 | 基于文本的请求/响应,通常是 JSON 或 HTML | 数据帧,支持文本和二进制数据 |
状态保持 | 无状态(每个请求都是独立的) | 有状态(连接保持,允许持续交互) |
头信息 | 每个请求都带有完整的 HTTP 头信息 | 头信息仅在握手时使用,后续数据传输不需要 |
适用场景 | 静态网页加载、API 请求等 | 实时聊天、在线游戏、数据流等 |
安全性 | 依赖于 HTTPS 加密 | 通过 WSS 进行加密,确保数据传输安全 |
心跳机制 | 无法主动检测连接是否存活 | 支持 Ping/Pong 心跳机制,确保连接活跃性 |
WebSocket 和 HTTP 各自有其适用的场景和优势。HTTP 适合传统的请求-响应模式,而 WebSocket 则更适合需要实时、双向交互的应用。在现代 Web 开发中,根据应用的需求选择合适的协议可以大大提高性能和用户体验。 |
应用场景
HTTP 的应用场景
- 静态网页加载: 用于加载 HTML、CSS、JavaScript 文件等静态资源。
- RESTful API: 适合请求-响应模型,常用于获取、创建、更新和删除数据。
- 文件下载和上传: 支持大文件的上传和下载,适合需要处理文件的场景。
- 搜索引擎: 用于搜索查询,返回结果数据。
- 内容管理系统: 用于管理和发布网站内容,支持用户交互。
WebSocket 的应用场景
- 实时聊天应用: 支持用户之间的即时消息传递,如聊天工具和社交平台。
- 在线游戏: 实时数据传输,确保游戏状态和玩家动作的即时更新。
- 金融交易平台: 实时更新股票价格、交易信息和市场动态,适合高频交易。
- 实时协作工具: 适合文档编辑、白板协作等场景,支持多人实时互动。
- 物联网(IoT)应用: 设备之间的实时数据交换和监控,适合智能家居和工业应用。
- 推送通知: 实时推送消息,如新闻更新、社交媒体通知等。 因此我们就可以得到以下结论:
- HTTP 更适合于请求-响应式的交互,适用于大多数常规的数据传输需求。
- WebSocket 则专为实时、双向的通信而设计,适合需要快速响应和持续连接的应用场景。选择合适的协议可以根据具体的应用需求和用户体验进行优化。
贡献者
版权所有
版权归属:PinkDopeyBug