websocket 作为现代浏览器的长连接标准,可以很好的解决浏览器与服务器实时通讯的问题,那么在 websocket 出现之前是怎么解决这个问题的呢?首先来回顾一下在此之前浏览器和服务器的”长连接”之路。
回顾
在 websocket 协议出来之前,主要是有三种方向去实现类似 websocket 的功能的。
Flash
flash 支持 socket 通讯功能,基于 flash 可以很简单的实现与服务器建立通讯。
- 优点:开发简单、兼容性高
- 缺点:需要浏览器启用 flash 功能,并且逐渐被浏览器淘汰
AJAX Polling
浏览器使用 ajax 去轮询服务器,服务器有内容就返回,轮询也分为短轮询和长轮询。
短轮询
短轮询即浏览器通过 ajax 按照一定时间的间隔去请求服务器,服务器会立即响应,不管有没有可用数据。
- 流程图:
- 优点:短链接、服务器处理方便。
- 缺点:实时性低、很多无效请求、性能开销大
长轮询
长短轮询则是浏览器通过 ajax 与服务器建立连接,服务器在没有数据返回时一直阻塞着,直到有数据之后才返回响应。
- 流程图:
- 优点:实时性高
- 缺点:每个连接只能返回一次数据
COMET
comet 也是常用的一种服务器推送技术,主要的原理是通过 HTTP Chunked 响应,将消息源源不断的推送给浏览器,通常情况下服务器返回的响应内容都是定长的,会使用 Content-Length 来指定响应报文的长度,而 Chunked 编码的响应则是通过一种特殊的编码,只要浏览器没有遇到结束标识,就会边解析边执行对应的响应内容。
- chunked 编码报文示例:
<code>HTTP/1.1 200 OK Content-Type: text/html Date: Thu, 08 Aug 2019 02:50:06 GMT Transfer-Encoding: chunked 11 callback('data1') 11 callback('data2') 0 /<code>
报文格式为:
<code>\r\n \r\n \r\n \r\n ... 0\r\n /<code>
由 16 进制的数字来标识一个 chunk data 数据的长度,在读取到 0\r\n 时结束,通过一直读取 chunk data 来执行 js 代码,从而向客户端推送数据。
- go 语言实现:
<code>func main() { http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") flusher := w.(http.Flusher) w.Write([]byte(" ")) flusher.Flush() // 延迟一秒,以便观察浏览器的边解析边执行 time.Sleep(time.Second) w.Write([]byte(" ")) flusher.Flush() }) log.Fatal(http.ListenAndServe(":8080", nil)) } /<code>
通过 iframe 实现
iframe 实现是通过隐藏一个 ,通过 iframe 连接到服务器,服务器响应带有