WebSocket 的产生

HTTP 不断轮询
怎么样才能在用户不做任何操作的情况下,网页能收到消息并发生变更。

最常见的解决方案是,网页的前端代码里不断定时发 HTTP 请求到服务器,服务器收到请求后给客户端响应消息。

这种方式的应用场景很多,例如扫码登录,前端网页不知道用户是否扫描,只能不断询问后端服务器。当这种方式就会有两个比较明显的问题:

  • F12 打开页面,会看到很多 HTTP 请求,会增加服务器负担;
  • 最坏情况下,就会有延迟,有明显卡顿。

长轮询
如果 HTTP 请求将超时设置的很大,比如 30 秒,在这 30 秒内只要服务器收到了扫码请求,就立马返回给客户端网页。如果超时,那就立马发起下一次请求。通过这种方法,减少 HTTP 请求个数,且能及时响应。

像这种发起一个请求,在较长时间内等待服务器响应的机制,就是所谓的长轮询机制。我们常用的消息队列 RocketMQ 中,消费者去取数据时,也用到了这种方式。

以上两种方法,本质上还是客户端主动请求数据。

WebSocket
TCP 连接的两端,同一时间里,双方都可以主动向对方发送数据。这就是所谓的全双工。然而最常用的 HTTP/1.1 只能有一方主动发送数据,是半双工。

新的应用层协议 WebSocket就被设计出来,就是为了满足全双工的需求。

浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信。

  • 如果此时是普通的 HTTP 请求,那后续双方就还是老样子继续用普通 HTTP 协议进行交互,这点没啥疑问。
  • 如果这时候是想建立 WebSocket 连接,就会在 HTTP 请求里带上一些特殊的 header 头。

Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n

浏览器想升级协议(Connection: Upgrade),并且想升级成 WebSocket 协议(Upgrade: WebSocket)。同时带上一段随机生成的 base64 码(Sec-WebSocket-Key),发给服务器。如果服务器支持,就会通过 WebSocket 握手流程,并根据客户端的 base64 码用某个公开算法变成另一个字符串,放在 HTTP 响应的 Sec-WebSocket-Accept 头里,同时带上 101 状态码,发回给浏览器。

HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

最终,客户端也用相同公开算法解析 base64 码,如果与回传的字符串相同那么久通过验证,建立 WebSocket 连接。

WebSocket 建立流程

总的来说,就是经历了三次TCP握手之后,利用 HTTP 协议升级为 WebSocket 协议。WebSocket只有在建立连接时才用到了HTTP,升级完成之后就跟HTTP没有任何关系了。

WebSocket 数据格式

关注的是如下几个数据:

  • opcode字段:这个是用来标志这是个什么类型的数据帧。
  • payload字段:存放的是我们真正想要传输的数据的长度,单位是字节。这里要注意,payload 长度可以只有 7 bit,也可以是 7 + 16 bit,也可以是7 + 64 bit,具体怎么读就是先读一开始的 7bit,如果是 0-125 范围内,那就是读完了;126(0x7E)那就是再读 16 bit;127(0x7F)就是再读 64 bit。
  • payload data字段:这里存放的就是真正要传输的数据,在知道了上面的payload长度后,就可以根据这个值去截取对应的数据。

WebSocket完美继承了 TCP 协议的全双工能力,并且还贴心的提供了解决粘包的方案。它适用于需要服务器和客户端(浏览器)频繁交互的大部分场景,比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

posted @ 2024-07-30 11:44  NETYZreal  阅读(7)  评论(0编辑  收藏  举报