HTTP 协议的通信只能由客户端发起。在一些场景下,这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用轮询:每隔一段时间,就发出一个询问,了解服务器有没有新的信息。
轮询的效率低,非常浪费资源。
ajax轮询
原理:让浏览器隔几秒就发送一次请求,询问服务器是否有新信息。
场景:来自知乎
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) —— loop
缺点:需要服务器有很快的处理速度和资源。(速度)
长轮询
原理:跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
场景:来自知乎
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
缺点:需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
WebSocket协议
WebSocket 是由 Web 浏览器和服务器之间的一种全双工通信协议。一旦 Web 服务器与客户端之间建立起 WebSocket 协议的通信连接,之后所有的通信都依靠这个专用协议进行。通信过程中可互相发送 JSON 、 XML 、 HTML 或图片等任意格式的数据。
WebSocket协议的主要特点:
- 推送功能:支持由服务器向客户端推送数据的推送功能。这样,服务器可直接发送数据,而不必等待客户端的请求。
- 减少通信量:只要建立起WebSocket连接,就希望一直保持连接状态。和HTTP相比,不但每次连接时的总开销减少,而且由于WebSocket的首部信息很小,通信量也相应减少了。
与HTTP协议的异同
相同点:
- 都是基于 TCP 的应用层协议;
- 都使用 Request/Response 模型进行连接的建立;
- 在连接的建立过程中对错误的处理方式相同,在这个阶段 WebSocket 可能返回和 HTTP 相同的返回码;
- 都可以在网络中传输数据。
不同点:
- WS 使用 HTTP 来建立连接,但是定义了一系列新的 header 域,这些域在 HTTP 中并不会使用;
- WS 的连接不能通过中间人来转发,它必须是一个直接连接;
- WS 连接建立之后,通信双方都可以在任何时刻向另一方发送数据;
- WS 连接建立之后,数据的传输使用帧来传递,不再需要 Request 消息;
- WS 的数据帧有序。
通信过程
TCP 连接建立后,客户端发送 websocket 的握手请求,请求报文头部如下:
GET /uin=xxxxxxxx&app=xxxxxxxxx&token=XXXXXXXXXXXX HTTP/1.1
Host: server.example.cn:443
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Upgrade: websocket
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: user_id=XXXXX
Sec-WebSocket-Key: 1/2hTi/+eNURiekpNI4k5Q==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: binary, base64
- 第一行为请求的方法,类型必须为 GET ,协议版本号必须大于 1.1
- Upgrade 字段必须包含,值为 websocket
- Connection 字段必须包含,值为 Upgrade
- Sec-WebSocket-Key 字段必须包含,记录着握手过程中必不可少的键值。是一个 Base64 encode 的值,这个是浏览器随机生成的。
- Sec-WebSocket-Protocol 字段必须包含,记录着使用的子协议。是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议。
- Origin:指明请求的来源。Origin 头部主要用于保护 WebSocket 服务器免受非授权的跨域脚本调用 WebSocket API 的请求。
响应:服务器接收到请求后,返回状态码为 101 Switching Protocols 的响应。
HTTP/1.1 101 Switching Protocols
Server: WebSockify Python/2.6.6
Date: Wed, 27 May 2020 03:03:21 GMT
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hXXXXXXXXXXXXXXxGmM=
Sec-WebSocket-Protocol: binary
- Sec-WebSocket-Accept 表示经过服务器确认,并且对客户端的 Sec-WebSocket-Key 进行了加密
- Sec-WebSocket-Protocol:表示最终使用的协议