Blogger Information
Blog 143
fans 1
comment 0
visits 440309
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
WebSocket协议解析
弘德誉曦的博客
Original
1231 people have browsed it

< id="ajax短轮询ajax-polling" style="margin: 10px 0px; padding: 0px; list-style-type: none; list-style-image: none; font-size: 16px; line-height: 1.5; color: rgb(68, 68, 68); font-family: Tahoma, Arial, Helvetica, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);">Ajax短轮询(Ajax Polling)Ajax短轮询即客户端周期性的向服务器发起HTTP请求,不管服务器是否真正获取到数据,都会向客户端返回响应。每个request对应一个response,由于HTTP/1.1的持久连接(建立一次TCP连接,发送多个请求)和管线化技术(异步发送请求),使得HTTP请求可以在建立一次TCP连接之后发起多个异步请求。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求在每次发送时都会带上很长的请求头部字段,其中真正有效的数据可能只是很小的一部分(如Cookie字段),显然服务器会浪费带宽等资源。有朋友可能会想,那可以加大Ajax的传输时间,如改为3s为一个周期。但是时间长了,对于实时性要求比较高的项目来说,页面更新的数据也就太慢了。Comet(服务端推送)而比较新的技术向服务器轮询获取数据的实现是Comet,即服务端推送。简单的说,服务端推送就是在客户端发起HTTP请求之后,服务器可以主动的向客户端推送数据。实现Comet的方式有两种:Ajax长轮询和HTTP流。Ajax长轮询(Ajax Long-polling)Ajax长轮询本身不是一个真正的推送。长轮询是短轮询的一种变体。在客户端向服务器发起HTTP请求之后,服务器并不是每次都立即响应:当服务器得到最新数据时,会向客户端传输数据;当数据没有更新时,服务器会保持这个连接,等待更新数据之后,才向客户端传输数据。当然,如果服务端数据长时间没有更新,一段时间后,请求就会超时。客户端收到超时信息后,会重新发送一个HTTP请求给服务器。也就是说,只有在服务器获取更新后的数据,才会向客户端传输数据。这种方式也存在弊端。虽然服务端可以主动的向客户端传输数据,但是依然需要反复发出请求(HTTP请求数量比短轮询少很多)。短轮询和长轮询的相同点在于客户端都需要向服务器发起HTTP请求,不同点在于服务器如何响应:短轮询是服务器立即响应,不管数据是否有效;长轮询是等待数据更新后响应。HTTP流HTTP流不同于轮询技术,HTTP流只建立一次TCP连接,在3次握手之后进行HTTP通信,此时客户端向服务器发起一个HTTP请求,而服务器保持连接打开,周期性的向客户端传输数据。双方在没有明确提出断开连接时,服务器就会持续向客户端传输数据。也就是说,假如服务器数据没有更新,服务器不会返回响应,而是保持连接;如果数据更新了,会立即将数据传输给客户端。此时会发起下一个HTTP请求,过程周而复始。在JS中,可以通过侦听readystatechange事件及检测readyState的值是否为3来实现HTTP流。随着不断从服务器接收数据,readyState的值会周期性的变为3。当readyState值变为3时,responseText属性就会保存接受到的所有数据。此时,就需要比较此前接收到的数据,决定从什么位置开始取得最新的数据。用XHR对象实现HTTP流的方式如下:let httpStream = (url, processor, finished) => {  let xhr = new XMLHttpRequest()  let received = 0
 xhr.open(url, 'get', true)
 xhr.addEvetntListener('readystatechange', () => {    let result    if (xhr.readyState === 3) {
     result = xhr.responseText.slice(received)
     received += result.length
     processor(result)
   } else if (xhr.readyState === 4) {
     finished(xhr.responseText)
   }
 })
}
只要readyState为3,就对responseText进行分隔以获取最新数据。这里的received表示记录已经处理了多少字符。然后通过processor回调函数来处理最新数据。而当readyState为4时,表示数据已经完全获取到,则直接将xhr.responseText传入finished回调函数处理即可。调用方式如下.httpStream(url, data => {  console.log(data)
}, finishedData => {  console.log(data)
})
对(长、短)轮询和HTTP流做一个小小的总结传统轮询技术(Ajax短轮询)是客户端向服务器发起HTTP请求,无论数据是否更新,服务器都会传输数据。一个request对应一个response。服务器推送技术(Ajax长轮询)是短轮询的变种,是客户端向服务器发起HTTP请求,只有等待数据更新后才会传输数据,否则服务器保持连接状态。接着发起下一次HTTP请求,一个request对应一个response。服务器推送技术(HTTP流),在客户端只发起一次HTTP请求,服务器保持连接状态,在数据更新之后,服务器会传输数据,否则保持连接状态。此时一个requset对应多个response。无论是短轮询、长轮询,还是HTTP流,相同点在于都需要客户端先发起HTTP请求。HTML5实现服务端推送由于服务器推送的重要性(实现赛事结果更新、聊天室等),HTML5实现了两个服务端推送接口,SSE和WebSocket。SSESSE(Server-Sent Eevents,服务器发送事件)用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。实现SSE有以下几点要求服务器响应的MIME类型必须是text/event-stream。必须按照指定的格式输出。用法如下,其实理解了服务器推送之后,SSE使用起来相对简单// EventSource接受的参数必须同源。// 使用message事件监听从服务器收到的消息,并存储在event.data对象里。let source = new EventSource('index.php')
source.onmessage = e => {  console.log(e.data)
}
SSE在IE下都不支持,ios4.0以上、android4.4以上都支持SSE。WebSocket铺垫了那么久的前文,终于到WebSocket了... :) 感谢各位朋友不嫌弃的耐心阅读。简单来说,WebSocket是一种协议,与HTTP协议一样位于应用层,都是TCP/IP协议的子集。HTTP协议是单向通信协议,只有客户端发起HTTP请求,服务端才会返回数据。而WebSocket协议是双向通信协议,在建立连接之后,客户端和服务器都可以主动向对方发送或接受数据。WebSocket协议建立的前提需要借助HTTP协议,建立连接之后,持久连接的双向通信就与HTTP协议无关了。WebSocket协议的目标是在一个独立的持久连接上提供全双工双向通信。客户端和服务器可以向对方主动发送和接受数据。在JS中创建WebSocket后,会有一个HTTP请求发向浏览器以发起请求。在取得服务器响应后,建立的连接会使用HTTP升级将HTTP协议转换为WebSocket协议。也就是说,使用标准的HTTP协议无法实现WebSocket,只有支持那些协议的专门浏览器才能正常工作。请认真阅读、记住上面一段话。: )由于WebScoket使用了自定义协议,所以URL与HTTP协议略有不同。未加密的连接为ws://,而不是http://。加密的连接为wss://,而不是https://。使用JavaScript是实现WebScoket协议相对简单,以下是WebSocket APIs// 打开WebSocket, 传递的参数url没有同源策略的限制。let websocket = new WebSocket(url)// 监听open事件,在成功建立websocket时向url发送纯文本字符串数据(如果是对象则必须序列化处理)。websocket.onopen = () => {  if (websocket.readyState === WebSocket.OPEN) {
   websocket.send('hello world')
 }
}// 监听message事件,在服务器响应时接受数据。返回的数据存储在事件对象中。websocket.onmessage = e => {  let data = e.data  console.log(data)
}// 监听error事件,在发生错误时触发,连接不能持续。websocket.onerror = () => {  console.log('websocket connecting error!!')
}// 监听close事件,在连接关闭时触发。只有close事件的事件对象拥有额外的信息。可以通过这些信息来查看关闭状态websocket.onclose = e => {  let clean = e.wasClean // 是否已经关闭
 let code = e.code // 服务器返回的数值状态码。
 let reason = e.reason //服务器返回的消息。}
注意,WebScoket不支持DOM2语法为事件绑定事件处理程序,因此必须使用DOM0级语法来每个事件绑定事件处理程序。// correct!
websocket.onerror = () => {}// error!
websocket.addEventListener('error', () => {})
看完了WebSocket APIs之后,再来看看WebSocket是如何实现连接的(奶思,看到这里的朋友耐心真棒.. 只剩下一点点了:) )WebSocket是应用层协议,是TCP/IP协议的子集,通过HTTP/1.1协议的101状态码进行握手。也就是说,WebSocket协议的建立需要先借助HTTP协议,在服务器返回101状态码之后,就可以进行websocket全双工双向通信了,就没有HTTP协议什么事情了。参照wiki握手协议的例子:并对一些字段进行说明。Connection:Connection必须设置为Upgrade,表示客户端希望连接升级Upgrade:Upgrade必须设置为WebSocket,表示在取得服务器响应之后,使用HTTP升级将HTTP协议转换(升级)为WebSocket协议。Sec-WebSocket-key:随机字符串,用于验证协议是否为WebSocket协议而非HTTP协议Sec-WebSocket-Version:表示使用WebSocket的哪一个版本。Sec-WebSocket-Accept:根据Sec-WebSocket-Accept和特殊字符串计算。验证协议是否为WebSocket协议。Sec-WebSocket-Location:与Host字段对应,表示请求WebSocket协议的地址。HTTP/1.1 101 Switching Protocols:101状态码表示升级协议,在返回101状态码后,HTTP协议完成工作,转换为WebSocket协议。此时就可以进行全双工双向通信了。WebSocket协议的浏览器兼容性较好。

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post