找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4667

积分

0

好友

645

主题
发表于 3 天前 | 查看: 17| 回复: 0

在传统的 Web应用 开发中,通信主要依赖于 HTTP 协议。这种通信模式通常是这样的:客户端主动向服务器发送一个 HTTP 请求,服务器处理完这个请求后,再向客户端回复一个 HTTP 响应。换句话说,HTTP 协议是一个典型的“请求-响应”模型,只能由客户端发起,服务器被动响应。

HTTP客户端与服务器请求响应时序图

然而,在很多实际的应用场景里,我们需要服务器能够主动向客户端推送信息。这时候,单纯的 HTTP 协议就显得有些力不从心了。举一个最常见的例子:实现一个 Web 聊天室。当有新的聊天消息到达时,服务器必须能够立刻通知在线的客户端。如果只用 HTTP,客户端就只能通过不断向服务器“轮询”(即定时发送请求)来询问是否有新消息。

轮询这种方式存在一个致命缺陷:性能和实时性难以兼得。如果轮询频率设置得太高,服务器将承受巨大的、不必要的请求压力;如果为了节省资源而降低轮询频率,消息通知的实时性又会大打折扣,用户体验变差。

显然,服务器主动推送数据是一个强需求,最好能在网络协议层面得到原生支持。为此,WebSocket 协议应运而生。

WebSocket,顾名思义,它为 Web 应用引入了 套接字(Socket) 般的通信能力。它是一个建立在 TCP 协议之上的应用层协议,为通信的双方提供了一个 全双工 的信道,允许服务器和客户端在任何时刻互相发送数据。

为了最大程度地兼容现有的 Web 基础设施(比如广泛部署的 HTTP代理 和防火墙),WebSocket 的设计非常巧妙。它复用了 HTTP 的 80 和 443 端口,并且连接建立阶段完全使用 HTTP 协议(通过一个特殊的 Upgrade 请求头)。这意味着,现有的 Nginx 等中间件可以无缝支持 WebSocket 协议,无需进行大规模的网络架构调整。如果你对网络协议栈的底层原理感兴趣,可以在 云栈社区 的网络技术板块找到更多深入的讨论。

如何表示 WebSocket 服务器地址?

和 HTTP 协议一样,WebSocket 服务器的地址也使用 URL 来表示,只是协议部分换成了 ws(非加密)或 wss(加密,类似 HTTPS)。

# ws 代表普通的 WebSocket 协议,默认使用 80 端口
ws://api.example.com/chat

# wss 代表安全的 WebSocket 协议,默认使用 443 端口
wss://api.example.com/chat

连接是如何建立的?

WebSocket 连接的建立过程可以称为“握手”,它始于一个标准的 HTTP 请求。

  1. 客户端发起升级请求:客户端首先通过 TCP 连接到服务器。然后,它通过这个 TCP 连接发送一个特殊的 HTTP GET 请求。这个请求的关键在于其头部,必须包含 Connection: UpgradeUpgrade: websocket,以此告知服务器:“我想把当前的 HTTP 连接升级为 WebSocket 连接”。

    GET /chat HTTP/1.1
    Host: localhost:8080
    User-Agent: Go-http-client/1.1
    Connection: Upgrade
    Sec-WebSocket-Key: bLPIf/xpAnrtCKuifPKTUg==
    Sec-WebSocket-Version: 13
    Upgrade: websocket
  2. 服务器同意升级:服务器接收到这个请求后,会检查 Upgrade 头部。如果服务器支持 WebSocket,它将回复一个状态码为 101 Switching Protocols 的 HTTP 响应,表示同意切换协议。

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

WebSocket协议握手升级流程图

一旦这次 HTTP 请求-响应交互完成,客户端和服务器之间的 TCP 连接就被“升级”了。此后,双方将不再使用 HTTP 协议,而是完全使用 WebSocket 协议帧来进行双向通信。

数据是如何传输的?—— 理解数据帧

连接建立后,通信的所有数据都被组织成“帧(Frame)”来传输。一条完整的应用层消息(比如一段聊天文本)可能会被拆分成一个或多个连续的帧。

WebSocket 数据帧的结构比较复杂,但其核心目的是高效、可靠地承载数据和元信息。

WebSocket数据帧头部结构示意图

一个数据帧主要包含以下几个部分:

  • 标志位(4位):
    • FIN (1位): 如果为1,表示这是当前消息的最后一帧。
    • RSV1, RSV2, RSV3 (各1位): 保留位,用于未来的协议扩展,通常为0。
  • 操作码(4位): 定义了帧的类型,这是理解帧功能的关键。
    • %x0 (0): 连续帧。表示这是一个分片消息的中间帧。
    • %x1 (1): 文本帧。表示帧内承载的是 UTF-8 编码的文本数据。
    • %x2 (2): 二进制帧。表示帧内承载的是任意的二进制数据。
    • %x3-7: 保留,用于未来的非控制帧。
    • %x8 (8): 连接关闭帧。用于优雅地关闭连接。
    • %x9 (9): Ping 帧。用于连接保活探测。
    • %xA (10): Pong 帧。用于响应 Ping 帧。
    • %xB-F: 保留,用于未来的控制帧。
  • 掩码位(1位): 指示“有效负载数据”是否被掩码处理。根据协议规范,所有从客户端发往服务器的帧必须置1(掩码),而服务器发往客户端的帧则通常为0。这主要是出于安全考虑,防止恶意代码或缓存污染。
  • 有效负载长度(7位): 这是一个变长字段,用于表示“有效负载数据”的字节数。
    • 如果值在 0-125 之间,它直接表示长度。
    • 如果值为 126,则实际的长度由后面2个字节(16位) 的扩展字段表示。
    • 如果值为 127,则实际的长度由后面8个字节(64位) 的扩展字段表示。
  • 扩展负载长度(0, 2 或 8字节): 如上所述,当基本长度字段为126或127时,用于存储实际长度。
  • 掩码密钥(4字节): 仅当掩码位为1时存在。客户端会生成一个随机的32位密钥,用于对有效负载数据进行异或掩码计算。
  • 有效负载数据: 帧实际承载的应用数据。

为了更直观地理解“有效负载长度”字段的三种情况,可以参考下图:

WebSocket数据帧长度字段三种情况示意图

注:上图为简化示意图,假设掩码位 MASK=0

简单来说,WebSocket 帧结构可以抽象为头部数据两部分。头部承载元信息,最重要的是操作码数据长度

根据操作码,帧可以分为两大类:

  1. 控制帧 (opcode 8, 9, 10): 用于管理连接本身。
    • close: 协商关闭连接。
    • ping / pong: 用于连接保活和心跳检测。一方发送 ping,另一方必须回复 pong。这能确保在网络空闲时连接依然活跃,并能及时探测到连接是否意外断开。
  2. 非控制/数据帧 (opcode 0, 1, 2): 用于承载实际的业务数据。
    • text: 传输文本数据。
    • binary: 传输二进制数据(如图片、文件、Protobuf消息等)。

核心要点总结

  1. 兼容性设计:WebSocket 完美兼容 HTTP 生态,利用 HTTP 协议来完成初始握手和协议升级。
  2. 两阶段通信
    • 握手阶段:使用 HTTP 协议(带 Upgrade 头)建立连接。
    • 通信阶段:切换为纯粹的 WebSocket 协议,进行全双工数据传输。
  3. 帧式传输:所有数据都封装在“帧”中传输,一个消息可由一帧或多帧组成。
  4. 帧结构:帧由头部有效负载数据构成。头部包含决定帧类型的操作码和表示数据大小的长度字段
  5. 帧分类:按操作码可分为控制帧(管理连接)和数据帧(承载业务数据)。
  6. 控制帧类型
    • ping/pong: 保活与心跳检测。
    • close: 优雅关闭连接。
  7. 数据帧类型
    • text: 用于文本协议(如 JSON)。
    • binary: 用于二进制协议,效率更高。

通过 WebSocket,开发者可以轻松构建需要实时、双向通信的 Web 应用,如在线聊天、实时通知、协同编辑、股票行情、在线游戏等,彻底摆脱了 HTTP 轮询带来的性能与实时性困境。




上一篇:使用Python的OpenAI客户端调用DeepSeek与阿里云百炼大模型API
下一篇:Apple Vision Pro深度体验:交互设计的革新与值得关注的槽点
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 18:03 , Processed in 0.757420 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表