- WebSocket简介 {#1-WebSocket简介} ===============================
WebSocket 是一种全双工通信协议,允许客户端和服务器之间建立持久化的双向通信连接。WebSocket 协议设计的初衷是解决 HTTP 协议在实时交互上的局限性,例如长轮询、Ajax 等方法的高延迟问题。WebSocket 可以在单个 TCP 连接上实现客户端与服务器之间的实时、低延迟的数据传输。
1.1 单工、半双工和双工 {#1-1-单工、半双工和双工}
这三个术语用于描述通信的方向性,主要适用于数据传输和网络通信。它们的定义如下:
-
单工(Simplex)
:单工通信是单向的,数据只能从发送方传送到接收方,而无法反向传输,通信过程不支持回复或反馈。-
电视广播:信号从发射台发送到电视,但观众无法向发射台发送信号。
-
传统的广播电台:信号从发射台发送到收音机,但听众无法向发射台发送信号。
-
-
半双工(Half-Duplex)
:半双工通信是双向的,但在任意时刻只能有一个方向的数据传输。发送方和接收方可以交替发送和接收数据,但不能同时进行。- 对讲机:一个人说话时,另一方需要等待,直到说话者结束才能回复。
-
双工(Full-Duplex)
:双工通信是完全双向的,数据可以同时在两个方向传输。发送方和接收方可以同时发送和接收数据。-
电话通话:两人可以同时讲话和听到对方的声音。
-
网络通信(如以太网)。
-
因此,关于单工、半双工、双工通信的方向性我们可以做如下总结:
- 单工:单向通信,无法反馈。
- 半双工:双向通信,但不能同时进行。
- 双工:双向通信,可以同时进行。
所以,我们可以根据不同的应用场景,选择合适的通信方式来提高数据传输的效率和用户体验。
1.2 Http的局限性 {#1-2-Http的局限性}
HTTP(超文本传输协议)是一种用于传输数据的协议,但它也有一些局限性,主要包括以下几点:
-
无状态性
HTTP 是一种无状态协议,每个请求都是独立的,不会保留客户端的上下文信息。这使得实现用户会话和状态管理变得复杂,需要额外的机制(如 cookies 或 sessions)。
-
性能问题
- 延迟:每次请求都需要重新建立连接,尤其是在使用 HTTP/1.1 时,存在多个请求时会造成额外的延迟。
- 带宽浪费:每次请求都需要发送请求头,增加了网络带宽的消耗。
-
安全性
加密不足:传统的 HTTP 传输是明文的,数据在传输过程中容易被窃听和篡改。虽然可以通过 HTTPS(HTTP Secure)来增强安全性,但 HTTP 本身并不提供加密。
-
不支持双向通信
HTTP 是请求-响应模型 ,客户端发起请求后,服务器才能响应,不支持实时双向通信。这在需要即时数据传输的应用中(如聊天应用)造成了限制。
-
请求重定向和缓存管理的复杂性
在复杂的应用中,HTTP 请求的重定向和缓存管理可能导致性能下降和数据一致性问题,增加了开发和维护的复杂性。
-
适应性差
HTTP 的设计初衷是用于静态网页的传输,对于现代动态内容和实时应用(如流媒体、在线游戏等)支持不够灵活。
虽然 HTTP 是互联网通信的基础,但其局限性促使开发者寻求更高效、更安全的替代方案,如 WebSocket、HTTP/2 和 HTTP/3 等。理解这些局限性有助于在设计系统时做出更明智的选择。
- WebSocket 协议 {#2-WebSocket-协议} =================================
2.1 协议标准 {#2-1-协议标准}
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 的基本结构:
|-----------|-----------------------------------|
| 1
| ws://[host]:[port]/[path]
|
可以看到 WebSocket 的 URL 一共由四部分构成,下面是关于它们的具体说明:
- 协议 :
ws://
:表示 WebSocket 协议。wss://
:表示 WebSocket Secure(加密的 WebSocket),类似于 HTTPS。
- 主机(host) :
- 服务器的域名或 IP 地址,例如
example.com
或192.168.1.100
。
- 服务器的域名或 IP 地址,例如
- 端口(port) (可选):
- WebSocket 默认端口是 80(ws)和 443(wss),可以省略。如果使用其他端口,则需要显式指定,例如
:8080
。
- WebSocket 默认端口是 80(ws)和 443(wss),可以省略。如果使用其他端口,则需要显式指定,例如
- 路径(path) :
- 服务器上用于 WebSocket 连接的具体路径,例如
/socket
。
- 服务器上用于 WebSocket 连接的具体路径,例如
下面是几个关于 WebSocket URL 格式的几个示例:
-
非加密的 WebSocket 连接:
|-----------|---------------------------------| |
1
|ws://example.com/socket
| -
加密的 WebSocket 连接:
|-----------|----------------------------------| |
1
|wss://example.com/socket
| -
指定端口的连接:
|-----------|--------------------------------------| |
1
|ws://example.com:8080/socket
| -
带路径的连接:
|-----------|---------------------------------------| |
1
|wss://example.com/api/v1/chat
|
WebSocket URL 的格式与 HTTP URL 类似,主要区别在于使用的协议前缀(ws
或 wss
)。使用时要确保服务器能够处理 WebSocket 连接,并在前端正确配置 URL。
2.2 工作原理 {#2-2-工作原理}
2.2.1 握手阶段 {#2-2-1-握手阶段}
WebSocket 连接从 HTTP 请求开始,客户端通过 HTTP 升级机制请求升级协议:
-
客户端发起 HTTP 请求,并在请求头中包含特定的 WebSocket 字段以表示希望建立 WebSocket 连接。
示例Http请求消息(部分):
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| |
1 2 3 4 5 6
|GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
|关键字段说明:
-
Upgrade: websocket
:表示希望将协议升级为 WebSocket。 -
Connection: Upgrade
:告知服务器此次连接希望协议升级。 -
Sec-WebSocket-Key
:客户端生成的随机密钥,用于服务器生成握手应答的Sec-WebSocket-Accept
。 -
Sec-WebSocket-Version
:指定 WebSocket 的版本,当前标准版本是13
。
-
-
服务器收到请求后,返回 101 状态码,并附带确认的握手信息。
示例 http响应消息(部分):
|-----------------|------------------------------------------------------------------------------------------------------------------------------------| |
1 2 3 4
|HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|关键字段说明:
-
HTTP/1.1 101 Switching Protocols
:状态行,它由三个部分组成:- 协议版本 :
HTTP/1.1
表示使用的 HTTP 版本。 - 状态码 :
101
是状态码,表示服务器已接受客户端的请求并正在切换协议。 - 状态描述 :
Switching Protocols
是对状态码的描述,说明了服务器的响应内容。
- 协议版本 :
-
Sec-WebSocket-Accept
:由Sec-WebSocket-Key
和特定算法生成的哈希值,用于确认握手的安全性。
-
2.2.2 数据帧传输 {#2-2-2-数据帧传输}
WebSocket 的数据通过帧 (frame) 进行传输("帧"是指在客户端和服务器之间传输的数据单元
)。每一帧都有固定的格式,包含数据以及控制信息。WebSocket 支持以下几种帧类型:
- 文本帧(Text Frame):用于传输文本数据,通常是 UTF-8 编码的字符串。
- 二进制帧(Binary Frame):用于传输二进制数据,如图片、音视频数据等。
- 关闭帧(Close Frame):用于关闭连接。
- Ping 和 Pong 帧:用于检测连接的活跃性,Ping 由客户端或服务器发送,Pong 由对方响应。
每个 WebSocket 数据帧的格式如下:
|------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 ... | +---------------------------------------------------------------+
|
- 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字段的值来确定长度。
2.2.3 保持连接与心跳 {#2-2-3-保持连接与心跳}
WebSocket 在建立连接后会保持连接状态,直到一方显式关闭连接。为了防止连接由于网络问题或闲置超时而断开,WebSocket 支持通过 Ping-Pong 机制 来检测连接的活跃性。客户端或服务器可以发送一个 Ping 帧,接收方必须在合理时间内响应一个 Pong 帧,确保连接仍然正常。
Ping-Pong 机制的示例流程:
- 客户端发送 Ping 帧 :
- 客户端发送一个 Ping 帧,Payload Data 可能包含当前时间戳。
- 帧格式:
FIN=1, Opcode=0x9, Mask=1, Payload Data=时间戳
- 服务器接收并响应 Pong 帧 :
- 服务器接收到 Ping 帧后,立即发送一个 Pong 帧,Payload Data 与接收到的 Ping 帧的 Payload Data 相同。
- 帧格式:
FIN=1, Opcode=0xA, Mask=0, Payload Data=时间戳
- 客户端接收 Pong 帧 :
- 客户端接收到 Pong 帧后,可以确认连接仍然活跃。
- 客户端可以根据时间戳计算往返时间(RTT),以评估网络延迟。
在使用 WebSocket 协议的 Ping-Pong 机制的时候,有以下事项需要再次强调:
-
双向检测:
客户端和服务器都可以发送 Ping 帧,并期望对方发送 Pong 帧。这使得双方都能检测到连接的活跃性。
-
超时处理:
如果发送 Ping 帧的一方在一定时间内没有收到 Pong 帧,可以认为连接已经断开或不活跃,并采取相应的措施(如关闭连接或重新连接)。
-
Payload Data:
Ping 和 Pong 帧的 Payload Data 是可选的,但通常会包含一些简单的数据(如时间戳),以便双方能够进行更详细的检测和分析。
2.2.4 关闭连接 {#2-2-4-关闭连接}
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 之间的代码(一定要注意避免与标准代码冲突)。
示例关闭帧字节流:
|-----------|--------------------------------------------------------------------------------------------------------|
| 1
| | FIN | RSV | Opcode | MASK | Payload length | Close Code (2 bytes) | Close Reason (n bytes) |
|
关闭流程非常简单就是发送和接收数据,所以对于应用层的 WebSocket 而言就只有两步:
- 发送: 当一方要关闭连接时,它会构造关闭帧并在 Payload 中写入状态码和可能的关闭原因,然后将其发送给另一方。
- 接收: 接收方在接收到关闭帧后,会读取 Payload,首先获取状态码,然后读取可选的关闭原因,从而知道连接关闭的具体情况。
如果是基于 TCP 的四次挥手进行描述就是以下四步:
-
发起关闭请求:
-
一方调用关闭操作
连接的一方(可以是客户端或服务器)决定关闭连接时,调用相应的
close()
方法。 -
发送关闭帧
主动断开连接的一方会创建并发送一个关闭帧(Close Frame),该帧包含关闭状态码和可选的关闭原因。
-
-
接收关闭帧:
-
接收帧
被动断开连接的一方(接收方)收到关闭帧时,会首先解析该帧的状态码和可选原因。
-
处理关闭帧
接收方可以根据关闭状态码进行相应的处理(例如,记录日志,更新连接状态等)。
-
-
发送响应的关闭帧 (Pong):
-
回应关闭请求
接收方在确认关闭请求后(可能进行清理操作)会发送自己的关闭帧,这通常是一个回应帧。
-
格式
该响应关闭帧也会包含一个状态码,通常是
1000
(正常关闭),并可能附带关闭原因。
-
-
最终关闭连接:
-
双方完成关闭
一旦双方都发送并接收了关闭帧,WebSocket 连接将被正式关闭。
-
清理资源
关闭连接时,双方可以释放相关资源,如定时器、事件监听器等。
-
2.2.5 安全性 {#2-2-5-安全性}
WebSocket 使用 wss
(WebSocket Secure)协议进行安全通信的方式与使用 ws
(WebSocket)协议类似,但需要额外的安全配置。
- WSS: 是通过 TLS/SSL 加密的 WebSocket 协议,类似于 HTTPS 相对于 HTTP 的形式。使用 WSS 可以确保数据在传输过程中得到加密,避免被窃听和篡改。
- 端口: WSS 通常使用 443 端口,而 WS(不安全)使用 80 端口。
在使用 WSS 时,必须提供有效的 SSL/TLS 证书。可以使用:
- 自签名证书: 用于开发和测试,但浏览器可能会显示安全警告。
- 公认的证书: 从受信任的证书颁发机构获取的证书,用于生产环境,以确保通信的安全性。
- webSocket和Http对比 {#3-webSocket和Http对比} =========================================
3.1 相同点和不同点 {#3-1-相同点和不同点}
相同点 {#相同点}
- 应用层协议 :
- WebSocket 和 HTTP 都属于应用层协议,主要用于数据的传输。
- 基于 TCP :
- 两者都依赖于 TCP 协议进行底层的数据传输,确保数据的可靠性和顺序。
- 跨平台兼容性 :
- WebSocket 和 HTTP 都可以在不同的平台和设备上使用,支持跨浏览器和跨设备的通信。
- 使用标准的 URL :
- WebSocket 和 HTTP 都使用类似的 URL 结构,例如
http://
和ws://
。
- WebSocket 和 HTTP 都使用类似的 URL 结构,例如
不同点 {#不同点}
| 特性 | HTTP | WebSocket | |----------|----------------------------|---------------------------| | 连接模式 | 短连接 | 长连接 | | 通信方式 | 单向(请求-响应) | 双向(全双工通信) | | 连接建立 | 每次请求建立新连接,之后关闭连接 | 初始通过 HTTP 握手后建立持久连接 | | 延迟 | 每次请求都需要重新建立连接,延迟较高 | 连接一旦建立后,延迟较低,可实时传输数据 | | 数据格式 | 基于文本的请求/响应,通常是 JSON 或 HTML | 数据帧,支持文本和二进制数据 | | 状态保持 | 无状态(每个请求都是独立的) | 有状态(连接保持,允许持续交互) | | 头信息 | 每个请求都带有完整的 HTTP 头信息 | 头信息仅在握手时使用,后续数据传输不需要 | | 适用场景 | 静态网页加载、API 请求等 | 实时聊天、在线游戏、数据流等 | | 安全性 | 依赖于 HTTPS 加密 | 通过 WSS 进行加密,确保数据传输安全 | | 心跳机制 | 无法主动检测连接是否存活 | 支持 Ping/Pong 心跳机制,确保连接活跃性 |
- 连接模式 :
- HTTP:每次请求都是独立的,需要重新建立连接,适合于一次性请求的场景。
- WebSocket:连接建立后可以保持很长时间,适合需要频繁数据交换的场景。
- 通信方式 :
- HTTP:客户端发起请求,服务器响应,适用于请求-响应模式。
- WebSocket:客户端和服务器可以随时发送消息,适合双向通信。
- 延迟 :
- HTTP:每次请求都需要经过建立和关闭连接的过程,导致较高的延迟。
- WebSocket:连接建立后,数据传输几乎是即时的,延迟较低。
- 数据格式 :
- HTTP:通常以文本格式传输,如 JSON、HTML 等。
- WebSocket:支持更灵活的数据格式,包括二进制数据,适合多种应用场景。
- 状态保持 :
- HTTP:每个请求都是独立的,无状态设计。
- WebSocket:连接是持久的,可以保持状态,适合需要长时间交互的应用。
- 心跳机制 :
- HTTP:没有内置机制检测连接的存活状态。
- WebSocket:通过 Ping/Pong 帧来保持连接活跃性,检测连接状态。
WebSocket 和 HTTP 各自有其适用的场景和优势。HTTP 适合传统的请求-响应模式,而 WebSocket 则更适合需要实时、双向交互的应用。在现代 Web 开发中,根据应用的需求选择合适的协议可以大大提高性能和用户体验。
3.2 应用场景 {#3-2-应用场景}
HTTP 的应用场景 {#HTTP-的应用场景}
- 静态网页加载 :
- 用于加载 HTML、CSS、JavaScript 文件等静态资源。
- RESTful API :
- 适合请求-响应模型,常用于获取、创建、更新和删除数据。
- 文件下载和上传 :
- 支持大文件的上传和下载,适合需要处理文件的场景。
- 搜索引擎 :
- 用于搜索查询,返回结果数据。
- 内容管理系统 :
- 用于管理和发布网站内容,支持用户交互。
WebSocket 的应用场景 {#WebSocket-的应用场景}
- 实时聊天应用 :
- 支持用户之间的即时消息传递,如聊天工具和社交平台。
- 在线游戏 :
- 实时数据传输,确保游戏状态和玩家动作的即时更新。
- 金融交易平台 :
- 实时更新股票价格、交易信息和市场动态,适合高频交易。
- 实时协作工具 :
- 适合文档编辑、白板协作等场景,支持多人实时互动。
- 物联网(IoT)应用 :
- 设备之间的实时数据交换和监控,适合智能家居和工业应用。
- 推送通知 :
- 实时推送消息,如新闻更新、社交媒体通知等。
因此我们就可以得到以下结论:
-
HTTP 更适合于请求-响应式的交互,适用于大多数常规的数据传输需求。
-
WebSocket 则专为实时、双向的通信而设计,适合需要快速响应和持续连接的应用场景。选择合适的协议可以根据具体的应用需求和用户体验进行优化。
附录 {#附录}
RESTful,通常被称为 REST(Representational State Transfer),是一种软件架构风格,用于设计网络应用程序。它基于HTTP协议,并遵循一组设计原则和约束,以实现可伸缩的、灵活的网络服务。
REST 的设计理念是:
- 资源 - 网络上的每个实体都是一个资源。资源可以是一个文档、一个人、一个事件等。
- 表现形式(Representations) - 资源的某个具体状态可以通过不同的表现形式来表示。例如,一个人的资源可以通过文本、图片或其他任何形式来表示。
- 状态转移(State Transfer) - 客户端通过发送请求到服务端,请求可能会改变服务端的状态(例如,创建新的资源,更新资源状态,读取资源状态等)。
RESTful 应用程序的典型特征包括:
- 使用 HTTP方法(如GET、POST、PUT、DELETE)来操作数据。
- 通过 URL(统一资源定位符)来表示资源的地址。
- 使用 标准数据格式(如JSON或XML)来描述资源的表现形式。
- 分离 客户端和服务器端状态,以便每个请求可以独立完成操作。
- 无状态的设计,每次请求都包含必要的信息,不依赖于之前的请求(这有助于实现高可伸缩性)。
- 超媒体作为自描述的指南,在响应中包含链接,以此作为导航的指南。
RESTful 设计原则通常用于创建现代网络上通信的 Web 服务和 API。由于其无状态性和分层架构,RESTful API 适合处理高并发请求,并且可以通过缓存和代理来改善性能和扩展性。