與Web服務器通訊 - Comet

  • XMLHttpRequest:可以從客戶端發送請求到服務器端而且獲得響應。一個小demo在這裏
    但XHR不適合快速來回發送消息(如聊天室),而且沒法將這一次調用和下一次調用聯繫起來(每次發起請求,服務器都要肯定請求來自何方)。html

  • 輪詢:瀏覽器按期向服務器發送消息並接受服務器回答是否有數據更新。(考慮輪詢間隔時間過長/太短的問題)html5

  • COMET(長輪詢):在每次請求的時候,服務器端保持該鏈接在一段時間內處於打開狀態,而不是在響應完成以後當即關閉。
    在打開狀態的這一段時間裏,有數據更新能夠當即獲得響應。
    上一個長鏈接關閉以後,瀏覽器當即打開一個新的長鏈接繼續請求。git

  • COMET(流):瀏覽器向服務器發送一個請求,而服務器保持鏈接打開,而後週期性向瀏覽器發送數據。github

  • Server-Sent events:適合服務器端向客戶端不斷的數據推送,更容易實現了comet。但整個通訊徹底單向,沒法知道瀏覽器是否響應。web

  • WebSocket:瀏覽器可以保持對Web服務器打開的鏈接,從而與服務器長時間交換數據。適合聊天室、大型多人遊戲、端到端寫做工具等。chrome

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來處理:

  • 一般就是在客戶端的頁面使用一個隱藏的窗口向服務端發出一個長鏈接的請求。服務器端接到這個請求後做出迴應並不斷更新鏈接狀態以保證客戶端和服務器端的鏈接不過時。經過這種機制能夠將服務器端的信息源源不斷地推向客戶端。

參考《試試跨域通訊》

相關文章
相關標籤/搜索