掘金 后端 ( ) • 2024-06-23 13:45

WebSocket协议是为了解决传统HTTP协议在实时双向通信方面的不足而设计的。它使得客户端和服务器之间可以在一个持久的连接上进行双向数据传输。以下是对WebSocket协议的底层原理的详细介绍。

1. WebSocket协议简介

WebSocket协议由IETF制定,并被RFC 6455标准化。它通过标准的HTTP/HTTPS端口(80/443)建立连接,但一旦连接建立后,通信协议将从HTTP切换到WebSocket协议。这使得它能够绕过防火墙和代理服务器的限制,保持持久的双向连接。

2. WebSocket连接建立过程

2.1 初始握手(Handshake)

WebSocket连接从HTTP握手开始。这是为了确保兼容现有的HTTP基础设施,如防火墙和代理。握手过程如下:

  • 客户端请求

客户端发送一个带有特殊头部的HTTP请求来发起WebSocket连接。以下是一个示例请求:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

关键头部解释:

  • Upgrade: websocketConnection: Upgrade 表明请求升级协议。

  • Sec-WebSocket-Key 是一个Base64编码的随机密钥,用于安全验证。

  • Sec-WebSocket-Version 指定WebSocket协议的版本(通常为13)。

  • 服务器响应

服务器验证请求并发送如下响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

关键头部解释:

  • 101 Switching Protocols 状态码表示协议切换成功。
  • Sec-WebSocket-Accept 是服务器根据客户端的 Sec-WebSocket-Key 生成的接受密钥。

2.2 密钥验证

服务器生成 Sec-WebSocket-Accept 的过程如下:

  1. Sec-WebSocket-Key 值与固定字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 连接。

固定字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 是WebSocket协议中用于生成 Sec-WebSocket-Accept 值的一部分,其目的是增强握手过程的安全性。通过使用这个固定字符串和客户端提供的 Sec-WebSocket-Key 进行SHA-1哈希和Base64编码,服务器可以生成一个唯一的响应键,以确保握手过程的安全和可靠。

  1. 对连接后的字符串进行SHA-1哈希计算。
  2. 将哈希结果进行Base64编码,得到 Sec-WebSocket-Accept 值。

例如:

import hashlib
import base64

key = "dGhlIHNhbXBsZSBub25jZQ=="
accept_key = base64.b64encode(hashlib.sha1((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode()).digest()).decode()
print(accept_key)  # s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

3. 数据帧传输

一旦握手成功,客户端和服务器之间的通信将使用WebSocket数据帧(frame)。每个帧包含以下字段:

  • FIN:1位,表示是否为消息的最后一个帧。
  • RSV1, RSV2, RSV3:各1位,保留字段,通常为0。
  • Opcode:4位,定义帧的类型(如文本帧、二进制帧、关闭帧、Ping帧、Pong帧)。
  • Mask:1位,表示是否对负载数据进行掩码(客户端发送的帧必须掩码)。
  • Payload length:7位(或更大),表示负载数据的长度。
  • Masking key:32位(如果Mask位为1),用于掩码/解码负载数据。
  • Payload data:实际传输的数据。

3.1 帧的类型(Opcode)

  • 0x0:延续帧,表示这是一个消息的后续帧。
  • 0x1:文本帧,表示这是一个文本数据帧。
  • 0x2:二进制帧,表示这是一个二进制数据帧。
  • 0x8:关闭连接帧,用于关闭连接。
  • 0x9:Ping帧,用于心跳检测。
  • 0xA:Pong帧,对Ping帧的响应。

3.2 帧的结构

例如,一个简单的文本帧("Hello")的二进制表示如下:

1000 0001  0000 0101  01001000 01100101 01101100 01101100 01101111
FIN = 1    Payload len = 5   Payload data ("Hello")
Opcode = 1 (Text)
Mask = 0 (No masking)

4. 连接管理

  • 心跳机制:通过Ping/Pong帧保持连接的活跃状态,防止中间设备关闭闲置连接。
  • 关闭连接:通过发送关闭帧(Opcode 0x8)优雅地关闭连接,可以附带关闭原因。

5. 安全性

WebSocket可以通过WSS(WebSocket Secure)协议加密传输,类似于HTTPS,从而保护数据的机密性和完整性。

6. 优点和挑战

优点

  • 实时性:相比于传统的轮询方式,WebSocket提供了实时的双向通信。
  • 效率高:减少了HTTP请求头的开销,节省了带宽和服务器资源。
  • 兼容性好:能够在现有的HTTP基础设施上运行。

挑战

  • 状态管理:需要服务器保持大量长连接,增加了状态管理的复杂性。
  • 安全性:长连接可能成为攻击目标,需要适当的安全措施。

通过上述对WebSocket协议底层原理的详细介绍,可以更好地理解其工作机制和应用场景,并在实际开发中更有效地利用WebSocket协议来实现实时通信。