Skip to content

3.6 既然有 HTTP 协议,为什么还要有 WebSocket?

服务器端给客户端推送消息的方式:

  • 使用 HTTP 不断轮询(定时轮询)

    • 最常见的就是扫码登录。如某信公众号平台,登录页面二维码出现之后,前端网页根本不知道用户扫没扫,于是不断去向后端服务器询问,看有没有人扫过这个码。而且是以大概 1 到 2 秒的间隔去不断发出请求,这样可以保证用户在扫码后能在 1 到 2 秒内得到及时的反馈,不至于等太久
    • 由于大量的http请求,消耗带宽,同时也会增加下游服务器的负担。
    • 如果轮询间隔较长,又会让用户体验到卡顿
  • 使用 HTTP 长轮询

    • 将HTTP请求超时设置的很大,比如 30 秒,这 30 秒内只要服务器收到了扫码请求,就立马返回给客户端网页。如果超时,那就立马发起下一次请求。

    • 减少了 HTTP 请求的个数,并且由于大部分情况下,用户都会在某个 30 秒的区间内做扫码操作,所以响应也是及时的。体验会更好。(如果超时,就需要用户刷新二维码,促进用户扫码速度)

    • 我们常用的消息队列 RocketMQ 中,消费者去取数据时,也用到了这种方式。

    • 这两种上面的集中方式只适用于简单的推送一些数据量比较少的数据,但是如果是网页游戏,需要大量推送就不合适了

  • HTTP虽然给予全双工的TCP,但是HTTP/1.1同一时间只有一方主动发送数据,且服务器不能主动推送

    • 因为设计之初是用来浏览网页文本,只需要客户端请求服务器响应即可

WebSocket

建立 WebSocket 连接

我们平时刷网页,一般都是在浏览器上刷的,一会刷刷图文,这时候用的是 HTTP 协议,一会打开网页游戏,这时候就得切换成我们新介绍的 WebSocket 协议

为了兼容这些使用场景。浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信。

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

这些 header 头的意思是,浏览器想升级协议(Connection: Upgrade),并且想升级成 WebSocket 协议(Upgrade: WebSocket)。同时带上一段随机生成的 Base64 码(Sec-WebSocket-Key),发给服务器。

如果服务器正好支持升级成 WebSocket 协议。就会走 WebSocket 握手流程,同时根据客户端生成的 Base64 码,用某个公开的算法变成另一段字符串,放在 HTTP 响应的 Sec-WebSocket-Accept 头里,同时带上 101 状态码(协议转换),发回给浏览器。HTTP 的响应如下:

http
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 只有在建立连接时才用到了 HTTP,升级完成之后就跟 HTTP 没有任何关系了

虽然HTTP/2也可以服务器主动推送数据,但是必须要客户端先发送请求才行

WebSocket 的消息格式

opcode 字段:这个是用来标志这是个什么类型的数据帧。比如。

  • 等于 1,是指 text 类型(string)的数据包
  • 等于 2,是二进制数据类型([]byte)的数据包
  • 等于 8,是关闭连接的信号

payload 字段:存放的是我们真正想要传输的数据的长度,单位是字节。比如你要发送的数据是 字符串"111",那它的长度就是 3。实际长度7+16bit+48bit,

  • 如果前7bit小于125,只用前七个
  • 126,那么真实长度放在后16bit中
  • 127:继续读取后面的32bit,即64bit

payload data 字段:这里存放的就是真正要传输的数据,在知道了上面的 payload 长度后,就可以根据这个值去截取对应的数据。

WebSocket 的数据格式也是数据头(内含 payload 长度) + payload data 的形式。解决粘包的"问题"。

WebSocket 的使用场景

它适用于需要服务器和客户端(浏览器)频繁交互的大部分场景,比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

回到文章开头的问题,在使用 WebSocket 协议的网页游戏里,怪物移动以及攻击玩家的行为是服务器逻辑产生的,对玩家产生的伤害等数据,都需要由服务器主动发送给客户端,客户端获得数据后展示对应的效果。

正在精进