服务端推送技术
HTTP 适用于客户端向服务端发送请求的情况,但是不太适用于服务端主动向客户端发送请求的情况。
如果获得服务端向客户端发送的数据,实现伪服务器推送的效果,客户端要轮询机制来实现这个效果。
最常见到的场景就是扫码登陆,比如网页端的微信登陆。

轮询分为两种,一种是短轮询,一种是长轮询。
短轮询的实现:客户端频繁的发送请求去询问服务端,服务端直接返回是否有结果,中间为 1-2s 的时间间隔。这样能保证响应可以在 1-2s 内可以做出响应。
短轮询的弊端:虽然短轮询可能消耗很少,但是依旧是消耗带宽;同时增加下有服务器的负担;用户存在 1-2s 的响应延时。
长轮询的实现:客户端发送请求询问服务端,服务端在有消息要传输时再返回结果。但也会有个最长时间,例如 30s。当超过 30s,客户端则会重新请求,例如下面的微信扫码登陆。长轮询就是解决短轮询的弊端。

RabbitMQ 中消费者在取数据的时候也用到了长轮询的机制。
上面这种需要服务端推送的技术称之为服务端推送技术。
Websocket 背景
上面的场景下,服务端向客户端推送的数据量较少,所以采用轮询的机制是可以应对的。但是,如果服务端和客户端之间需要频繁的双向发送数据呢?例如,游戏场景下,两者需要频繁的更新游戏内事物的状态。
TCP 是支持全双工的协议,但是 HTTP 在设计的时候,只考虑了客户端请求服务端的场景,使得 HTTP 是一个半双工的机制。为了更好支持大量数据的场景,需要一个基于 TCP 的新的应用协议,因此 Websocket 协议诞生了。
兼容 HTTP 与 Websocket 协议
在浏览器浏览网站时,为了兼容 HTTP 和 Websocket 协议,浏览器在建立连接后,先进行 HTTP 协议通信。如果确实是 HTTP 协议请求,那么继续使用 HTTP 协议通信。如果客户端想要升级 Websocket 协议,那么在 Header 中带下面字段:
Connection: Upgrade
Upgrade: Websocket
Sec-Websocket-Key: xxx # 随机生成的 base64 编码如果客户端支持 Websocket 协议,那么就会走 Websocket 握手流程。同时根据客户端生成的 base64 码,使用公开算法变成另一个字符串,放在响应的 Sec-Websocket-Accept 中,具体如下:
HTTP/1.1 101 Switching Protocols
Sec-Websocket-Accept: xxx # 生成的 base64
Upgrade: Websocket
Connection: Upgrade服务端在接收到后,同样会用公开算法来计算生成 base64 码,判断和接收到的是否一致。如果一致,则建立成功。

Websocket 数据包
Websocket 数据包在 ws 中被叫做帧,具体结构如下:

-
opcode:标识什么类型的数据帧。1 为 text,2 为二进制,8 为关闭连接的信号。 -
可以存放多个 Payload 长度。
-
若前 7bit 小于 126,则前 7bit 为真实 payload 长度;

-
如果前 7bit 为 126,则表示 payload 长度在 126 ~ 65535 之间,接下来 16bit 包含真实 payload 长度。

-
如果前 7bit 为 127,那么表示 payload 长度大于 65535。能放 2 的 64 次方数据,即很多 TB 肯定够用。

Websocket 同样是采用消息头+消息体的方式。其中一个原因就是因为 TCP 存在粘包的问题,为了解决这个,上层协议一般就采用消息头+消息体的方式。通过消息头中的消息体长度,就可以截取消息体,获得真正的长度。
Websocket 实际使用遇到的问题
Websocket 发送的时候,如果接收方接收阻塞,会导致发送方发送数据也阻塞。可以尝试对应的 Websocket 库来获得当前缓冲区大小。
- 接收端缓冲区满:如果接收端的缓冲区已满,它可能会暂停接收新的数据。在这种情况下,发送端可以继续尝试发送数据,但实际发送的数据可能会被接收端丢弃或者排队等待处理。如果接收端长时间不处理这些数据,可能会导致发送端的缓冲区也填满。
- 发送端缓冲区满:如果发送端的缓冲区已满,它将无法继续发送更多的数据,直到接收端开始处理数据并释放缓冲区空间。
