Fork me on GitHub

与Web服务器通信 - Comet

  • XMLHttpRequest:能够从客户端发送请求到服务器端并且得到响应。一个小demo在这里
    但XHR不适合快速来回发送消息(如聊天室),并且无法将这一次调用和下一次调用联系起来(每次发起请求,服务器都要确定请求来自何方)。

  • 轮询:浏览器定期向服务器发送消息并接受服务器回答是否有数据更新。(考虑轮询间隔时间过长/过短的问题)

  • COMET(长轮询):在每次请求的时候,服务器端保持该连接在一段时间内处于打开状态,而不是在响应完成之后立即关闭。
    在打开状态的这一段时间里,有数据更新可以立即得到响应。
    上一个长连接关闭之后,浏览器立即打开一个新的长连接继续请求。

  • COMET(流):浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性向浏览器发送数据。

  • Server-Sent events:适合服务器端向客户端不断的数据推送,更容易实现了comet。但整个通信完全单向,无法知道浏览器是否响应。

  • WebSocket:浏览器能够保持对Web服务器打开的连接,从而与服务器长时间交换数据。适合聊天室、大型多人游戏、端到端写作工具等。

Comet

我们通常说 Ajax 是一种页面向服务器请求数据的技术,那么 Comet 就是一种服务器向页面推送数据的技术,让信息以近乎实时地推送到页面上。

两种实现 Comet 的方式:

长轮询(long polling)

  1. 先说说短轮询的方式:
    浏览器向服务器发出请求,打开连接,看有没有更新的数据,没有就返回空;好,一次连接关闭。
    间隔一个轮询时间周期,浏览器又再次发出请求,再看有没有更新数据,如此反复。
    可见,这种模式效率不高,如果大多数情况下没有数据,还是得不断发送请求。
    这种实现方式很容易想到的就是 XHR + setTimeout。

  2. 鉴于这样的浪费,于是有了改善的方案——长轮询:
    浏览器发起一个请求,然后服务器一直打开连接,知道有数据更新可发送,再返回数据,关闭连接。
    随后又发送一个新的请求到服务器,不断重复这一过程。
    可见,这种模式减少了http请求的次数,优化了性能。

  3. But,果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。
    它们都有一个共同点:在浏览器接收数据,先对服务器发出请求。最大区别是服务器如何发送数据(立即发送/等待发送)。

流(Http Streaming)

  • 流的工作过程:
    浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性向浏览器发送数据。
    这个过程,连接不会关闭。这就是流不同于轮询的地方:它在页面的整个生命周期中只使用了一个http连接。
    实现方式很容易想到:利用 XHR,通过 readystatechange 事件并检测 readyState 是否为3进行操作。

  • 客户端实现:

    <script>
    // comet http streaming 的实现
      function createStreamingClient(url, progress, finished) {
        var xhr = new XMLHttpRequest(),
            received = 0;

        xhr.open("get", url, true);
        xhr.onreadystatechange = function() {
          var result;

          if (xhr.readyState == 3) {
            result = xhr.responseText.substring(received);
            received += result.length;

            progress(result);
          } else if (xhr.readyState == 4) {
            finished(xhr.responseText);
          }
        };

        xhr.send(null);
        return xhr;
      }

      var client = createStreamingClient("http://localhost:3000/streaming", function(data) {
                      alert("received:" + data);
                   }, function(data) {
                      alert("done");
                   });
    </script>
  • 服务器端实现:
var http = require("http");
var url = require("url");

http.createServer(start).listen(3000);

function start(req, res) {

	res.writeHead(200, {  
	    'Access-Control-Allow-Origin': '*',
	    'Content-Type': 'text/html'
	});

	var count = 0;  
	var sid = setInterval(function(){  
	    res.write(Math.random() + "");

	    if (++count == 5) {
	        clearInterval(sid);
	        res.end();
	    }
	}, 1000);
}

'Content-Type': 'text/html'主要是为了解决在chrome下输出数据太少被缓存起来的问题。

这种实现方式,有兼容问题,需要针对不同的浏览器设计不同的方案来改进用户体验(IE)。
IE 的实现是通过iframe来处理:

  • 通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。

参考《试试跨域通信》

posted on 2015-11-24 20:19  travelling-wxy  阅读(205)  评论(0)    收藏  举报