云计算百科
云计算领域专业知识百科平台

websocket 协议升级过程中客户端和服务器在背后都干了些啥

阶段一:连接握手阶段 (Handshake)

这个阶段的目标是在现有的 TCP 连接上,将应用层协议从 HTTP 升级为 WebSocket。

步骤 1: 建立 TCP 连接
  • 客户端:
  • 解析目标 WebSocket 服务器的 URL(例如 ws://example.com:8080/chat)。
  • 发起标准的 TCP 三次握手,与服务器的指定端口(通常是 80 for WS, 443 for WSS)建立一条可靠的、面向连接的传输通道。
  • 服务器:
  • 在指定端口监听传入的连接请求。
  • 接受客户端的 TCP 连接请求,完成三次握手。此时,一条双向的字节流通道已经建立,但双方默认仍使用 HTTP 协议进行通信。
步骤 2: 客户端发送 HTTP 升级请求
  • 客户端:
  • 通过已建立的 TCP 连接,发送一个标准的 HTTP GET 请求。
  • 但这个请求包含了一系列特殊的头字段,表明其意图是升级协议:
    • Upgrade: websocket:明确表示希望升级到 WebSocket 协议。
    • Connection: Upgrade:表示这是一个协议升级请求。
    • Sec-WebSocket-Key: 一个随机生成的 16 字节值,经过 Base64 编码。这是客户端的“挑战码”,用于验证服务器是否真的是一个 WebSocket 服务器,防止意外的跨协议攻击。
    • Sec-WebSocket-Version: 指定支持的协议版本(如 13)。
    • Origin: 发起请求的页面来源,用于服务器的同源策略检查。
    • (可选)Sec-WebSocket-Protocol:请求使用子协议(如 soap, wamp)。
    • (可选)Sec-WebSocket-Extensions:请求使用扩展(如压缩)。
  • 服务器:
  • 网络层接收到 TCP 数据包并重组为完整的 HTTP 请求。
  • 应用程序(如 Spring 的 WebSocketHandler)解析这个 HTTP 请求。
  • 服务器进行验证:
    • 检查 Upgrade 和 Connection 头是否存在且值正确。
    • 检查 Sec-WebSocket-Version 是否支持。
    • 检查 Origin 是否被允许(安全策略)。
    • 检查请求的路径(如 /chat)是否有对应的端点(Endpoint)处理。
步骤 3: 服务器响应升级请求
  • 服务器:
  • 如果所有检查通过,同意升级,则构造一个特殊的 HTTP 响应。
  • 响应的核心是状态码 101 Switching Protocols。任何其他状态码(如 200, 404)都表示升级失败。
  • 响应必须包含以下头字段:
    • Upgrade: websocket:确认升级到 WebSocket 协议。
    • Connection: Upgrade:确认这是对升级请求的响应。
    • Sec-WebSocket-Accept: 这是服务器的“应答码”。服务器使用标准算法:将客户端的 Sec-WebSocket-Key 与全局唯一的 GUID字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接,然后计算其 SHA-1 哈希值,最后将哈希值进行 Base64 编码,生成这个值。
  • (可选)如果客户端请求了子协议或扩展,服务器也会在 Sec-WebSocket-Protocol 和 Sec-WebSocket-Extensions 头中确认选择。
  • 将此 HTTP 响应通过之前建立的 TCP 连接发送回客户端。
  • 客户端:
  • 接收到服务器的 HTTP 响应。
  • 进行关键验证:
    • 必须检查状态码是否为 101。如果不是,则触发 onerror 事件并终止流程。
    • 必须验证 Sec-WebSocket-Accept 的值。客户端使用相同的算法计算它应该得到的值,并与服务器返回的值进行比对。如果不一致,说明对方不是一个合法的 WebSocket 服务器,连接将被视为失败。这是防止代理错误转发请求的关键安全措施。
  • 如果所有验证通过,客户端的 onopen 事件被触发,表示 WebSocket 连接已成功建立。

至此,协议升级完成。 underlying TCP 连接被保留,但之后的通信将完全使用 WebSocket 协议的数据帧格式,不再是 HTTP。


阶段二:数据交换阶段 (Data Transfer)

握手成功后,连接进入全双工通信模式。

步骤 4: 发送和接收消息 (Frames)
  • 发送方(可以是客户端或服务器):
  • 应用程序调用发送 API(如 WebSocket.send())。
  • WebSocket 库将应用层的消息(Message)(如一个字符串 “Hello”)打包成一个或多个数据帧(Frames)。
  • 帧的结构非常精简:
    • Opcode:操作码,指明这是文本帧、二进制帧、还是控制帧(如关闭、Ping/Pong)。
    • Payload length: payload(有效载荷)的长度。
    • Masking key:(仅限客户端->服务器)一个随机密钥,用于掩码加密 payload,防止代理缓存污染攻击。
    • Payload:实际要发送的数据。
    • (更多细节:FIN位表示是否是消息的最后一帧,RSV位用于扩展)
  • 将这些帧通过 TCP 连接发送出去。
  • 接收方:
  • 从 TCP 流中读取到来的原始字节。
  • 根据 WebSocket 协议规范解析帧头,了解帧的类型、长度等信息。
  • 如果帧被掩码(来自客户端),使用帧头中的 Masking-key 对 Payload 进行解掩码操作。
  • 根据 Opcode 处理帧:
    • 如果是数据帧(文本/二进制),将其追加到当前消息的缓冲区。
    • 如果是控制帧(如Ping),立即回复一个Pong帧;如果是Close帧,则开始关闭流程。
  • 当一个完整的消息(可能由多个帧组成)被接收后,将其解码(如将UTF-8字节解码为字符串)并通过 onmessage 事件传递给应用程序。

这个过程可以同时、异步地进行:客户端可以在发送数据的同时接收服务器发来的数据,实现了真正的全双工通信。


阶段三:连接关闭阶段 (Closure)

步骤 5: 关闭连接
  • 发起方(可以是任一方):
  • 决定关闭连接,调用 close() 方法或因为超时等原因需要关闭。
  • 构造一个关闭帧(Close Frame)。这个帧的 Payload 可以包含一个状态码和关闭原因(可选)。
  • 将该帧通过 TCP 连接发送给对方。
  • 接收方:
  • 收到关闭帧后,解析它。
  • 通常会回送一个同样的关闭帧作为确认(ACK)。
  • 触发本端的 onclose 事件。
  • 双方:
  • 在发送和收到关闭帧后,双方都知道连接即将终止。
  • 最终,底层 TCP 连接的关闭流程(四次挥手) 会被触发,释放所有资源。

总结一下整个过程中最重要的角色:

  • TCP连接:负责建立可靠的字节流通道,它是所有通信的物理基础。
  • HTTP:只负责最初的“敲门”和“自我介绍”(握手),门开了它就退场了。
  • WebSocket协议:负责握手成功后所有的应用层通信规则,包括如何封装数据帧、如何掩码、如何控制连接,它决定了通信的高效性和实时性。
赞(0)
未经允许不得转载:网硕互联帮助中心 » websocket 协议升级过程中客户端和服务器在背后都干了些啥
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!