1. WebSocket是什么
WebSocket是一种在单个TCP连接上进行全双工通信的协议。
WebSocket允许客户端和服务器之间进行实时的双向通信。这意味着服务器可以主动推送数据到客户端,而不需要客户端每次都发送请求来获取数据。这种通信方式通过长连接实现,即连接建立后,可以持续进行数据传输,直到一方主动关闭连接。
1.1 什么是实时通信 ?
传统的通信方式,比如电子邮件或者普通的网页浏览,通常都会存在一定的延迟。需要用户主动地请求来获取更新数据。
而实时通信,通过利用及时传输的技术和协议,使得数据能够立即传输给接收方。他也因此普遍被运用在及时聊天,在线会议,协同编辑等环境下。
通过建立全双工的持久连接,客户端和服务器之间,就能实现高效、实时性更强的通信。
可以说WebSocket的出现,就是为了解决实时通信的问题。
1.2 在WebSocket出来之前,实时通信的方案
在WebSocket出来之前,实时通信问题主要通过以下几种技术来解决 :
- 轮询 : 客户端定期向服务器发送请求,询问是否有新的数据可以用。服务器在接收到请求后,再检查是否有更新的数据,并将其返回给客户端。
- 这种做法会产生大量的请求和响应,导致不必要的网络开销和延迟。
- 长轮询 : 在客户端发起请求后,设定超长的超时时间(比如30秒),保持连接打开一段时间,等待新数据响应后再关闭连接。
- 比如百度网盘扫码,就是这么做的
- 虽然解决了无效轮询的数量,但还是需要频繁的建立和关闭连接。
- Comet : 同样是基于HTTP的技术,不同的是 : 在返回请求后继续保持连接打开。
- 核心思想就是通过保持长连接,来模拟实时通信,并允许服务器通过流式传输,frame等推送技术,来主动向客户端推送数据。
- 不过Comet虽然可以模拟实时通信,但它仍然是基于HTTP的模型。
- 在Comet中,服务器推送数据给客户端的方式,通常还是通过延长响应,或使用推送技巧来实现的。
1.3 WebSocket的优势
总的来说,WebSocket相比起其他的技术,有着以下这些优势 :
- 实时通信 : WebSocket允许在单个长时间的连接上,进行双向实时通信。在需要快速实时更新的应用程序里,WebSocket会比HTTP更加高效。
- 降低延迟 : WebSocket一旦建立连接,就会保持开放,数据可以在客户端和服务器之间以比HTTP更低的延迟进行传输。
- 更高效的资源利用 : 可以减少重复请求和响应的开销,因为它的连接只需要建立一次。
WebSocket的出现,填补了HTTP在实时通信方面的不足。他允许客户端和服务器,通过单个TCP连接进行双工通信,并且进行实时的数据交换。
目前的WebSocket技术,已经得到了主流浏览器的支持。而且由于WebSocket的标准定义了一套通信规范,所以无论是JavaScript还是Java、Python等各种语言,都存在相应的库。
1.4 WebSocket有哪些应用场景
WebSocket协议非常适用于基于Web的游戏、聊天应用,以及任何需要低延迟、实时连接的应用程序。比如 :
- 实时通信应用:
如在线聊天、在线会议等场景,用户可以实时地发送和接收消息。在这些应用中,WebSocket 提供的双向通信能力可以让用户快速地得到反馈,并且服务器能够及时推送新消息,避免了像 HTTP 轮询那样频繁请求造成的资源浪费。 - 实时数据推送应用:
包括股票交易平台、体育赛事实时比分推送等。这些应用需要将实时的数据及时地传递给用户,WebSocket 的持久连接和实时推送功能使其成为理想的选择。 - 在线游戏:
对于多人在线游戏,玩家的操作和游戏状态的更新需要实时双向传递。WebSocket 能够很好地满足这种需求,保证游戏的流畅性和实时性。
2. WebSocket的工作原理
HTTP对单独的请求使用单独的连接,他增加了服务器的负载,因为服务器必须为每个请求创建新的握手。一旦请求完成,连接就会关闭。
而WebSocket,只要任何一方不中断连接,连接就是持久的。
WebSocket协议是一个独立的,基于TCP的协议。它与HTTP的唯一的关系是,HTTP服务器将其握手解析为升级请求。
WebSocket握手过程,是客户端和服务器之间建立实时通信通道的关键步骤。它利用熟悉的HTTP协议进行初始连接,然后协商升级到WebSocket协议。
具体连接步骤如下 :
2.1 客户端发起握手请求
客户端向服务器发起一个特殊的HTTP GET请求,这个HTTP请求包含一些关键的头部信息,用于表明客户端希望将通信协议从 HTTP 升级到 WebSocket。
Upgrade:WebSocket
: [重要] 升级WebSocket标头,告诉客户端想要升级到WebSocket连接。Connection:Upgrade
: [重要] 连接升级标头通知服务器希望从当前连接协议进行升级。Sec-WebSOcket-Key
: 密钥,是包含随机生成的,本次握手特有的Base64编码的字符串的标头。服务器稍后会使用它来验证握手。- 还有一些额外的标头,具体取决于服务器的实现和所需的功能。
Sec-WebSocket-Version
:WebSocket
版本,目前主流的是13版本Sec-WebSocket-Protocol
: 用于指定客户端希望与服务器通信时所使用的子协议
然后客户端等待服务器对于HTTP升级请求的相应。
2.2 服务器响应并完成握手
服务器收到客户端的请求后,会检查请求头部是否符合WebSocket握手的要求。
- 比如服务器会验证
Upgrade:WebSocket
和Connection:Upgrade
是否存在
如果符合,服务器会生成一个特殊的响应来完成握手过程。响应的状态码是101,表示协议正在切换。
响应头部也会包含“Upgrade”和“Connection”字段,其值与客户端请求中的相同。
此外,服务器会解析 “Sec - WebSocket - Key” ,使用这个密钥来生成一个 “Sec - WebSocket - Accept” 响应头。并将其放在响应头部中返回给客户端。
2.3 客户端确认
一旦客户端收到,带有正确Sec
标头的服务端相应,它就会验证该相应,确认握手成功。
客户端和服务器,现在可以通过已建立的TCP连接,使用WebSocket协议交换数据,HTTP本质上被WebSocket取代。
3. WebSocket报文格式解析
WebSocket有一个默认的URI格式,可以是ws:
,也可以是wss:
- 端口是可选择的,默认端口80用于
ws:
,443端口用于wss:
- wss用于安全,因为设置了安全标志,并且在服务器和客户之间完成TLS握手,以实现安全通信
- 在WebSocket协议中,数据是使用帧序列来传输的
- WebSocket中的真实客户端和服务器之间,交换数据的基本单位,并且每个框架都有特定的结构。
3.1 FIN : 表示消息是否结束
- 当 FIN 位的值为 1 时,表示当前帧是消息的最后一个帧。例如,一个简单的文本消息可能只需要一个帧来传输,此时这个帧的 FIN 位会被设置为 1。
- 当 FIN 位的值为 0 时,表示消息还没有发送完,后面还有紧跟的帧。这在发送较长消息时非常有用,因为 WebSocket 允许将一个消息分割成多个帧进行发送。
3.2 RSV1、RVS2、RVS3
为了将来协议扩展而预留的空间,目前通常设置为0
3.3 opcode : 操作码
- 0x00(十进制为 0) - 延续帧 : 当一个消息被分割成多个帧发送时,除了第一个帧之外的其他帧都使用
opcode = 0x00
- 0x01(十进制为 1) - 文本帧 : 用于发送 UTF - 8 编码的文本数据
- 0x02(十进制为 2) - 二进制帧 : 用于发送二进制数据
- 0x09(十进制为 9) - 心跳帧 : 用于发送心跳请求,也就是检查连接是否还存活。接收方收到心跳请求(opcode = 0x09)后应该回复一个opcode = 0x0A 的帧,以确认连接正常。
3.4 mask : 掩码
掩码机制是用于对从客户端发送到服务器的数据帧进行加密处理的一种机制。主要是为了防止代理服务器、中间件等第三方设备被恶意利用来缓存和篡改数据。
- mask : 掩码,是一个比特(bit)的标志位,用于指示是否需要对数据载荷(Payload)进行掩码操作。如果mask为1,那么会在数据帧中定义一个掩码键(masking key),使用这个掩码键来对数据载荷进行掩码或反掩码操作。
- masking key : 掩码密钥,是一个 32 位(4 字节)的数据,是一个用于掩码操作的密钥。
- masking key与数据载荷进行特定的二进制运算(如XOR运算),从而实现对数据的掩码或反掩码。
- 这种运算过程对于客户端和服务端来说是公开的,但具体的masking key是未知的,这确保了数据的传输
安全。
3.5 payload length : 有效负载长度
用于告诉接收方这个帧中实际承载的数据长度是多少
3.6 payload : 有效载荷
是指 WebSocket
帧中包含的实际数据部分。这部分数据是通信双方真正想要传输的内容
4. WebSocket中的片段 : Fragments
片段(Fragments
)是指将一个完整的消息分割成多个 WebSocket
帧来进行发送的情况。当消息内容过长或者需要分块传输时,就会使用到片段。
FIN
标志位,用于指示这是否是消息的最后一个片段。如果FIN
为1
,则表示该帧是消息的结尾;如果FIN
为0
,则表示该帧是消息的一个中间片段。
5. Sec-WebSocket-Key为什么能确保服务器只接受来自合法客户端的 WebSocket 升级请求 ?
Sec - WebSocket - Key
是一个 Base64编码
的随机值。在每次客户端请求建立 WebSocket 连接时,这个值都是不同的。这种随机性使得攻击者很难猜测出正确的密钥来伪装成合法客户端。例如,它类似于一个每次进门都随机生成的 “门禁密码”,只有拥有正确密码(由客户端生成)的请求才能被服务器视为可能是合法的升级请求。
这种验证机制是 WebSocket 协议标准
的一部分。因为协议规定了双方都必须按照这个固定的方式进行密钥处理和验证,所以只要客户端和服务器都正确地实现了协议,就可以确保只有合法生成 Sec - WebSocket - Key
的客户端能够通过服务器的验证。
假设一个恶意攻击者试图伪造请求,它很难生成一个能够通过服务器验证的 Sec - WebSocket - Key
。因为 SHA - 1
哈希算法是单向的,攻击者几乎不可能从期望的 Sec - WebSocket - Accept
值反推出正确的 Sec - WebSocket - Key
。而且,由于 Sec - WebSocket - Key
是随机生成的,攻击者也很难猜测出一个有效的密钥来通过验证。
6. 参考
websocket是什么?和HTTP是什么区别?长轮询是什么?服务器推是什么?_哔哩哔哩_bilibili
10 分钟 理论 + 实操 搞懂 WebSocket_哔哩哔哩_bilibili
WebSocket的工作原理_哔哩哔哩_bilibili
WebSockets原理,握手和代码实现!用Socket.io制作实时聊天室_哔哩哔哩_bilibili