webSocket(二) 短輪詢、長輪詢、Websocket、sse

簡介

Web Sockets定義了一種在經過一個單一的 socket 在網絡上進行全雙工通信的通道。僅僅是傳統的 HTTP 通信的一個增量的提升,尤爲對於實時、事件驅動的應用來講是一個飛躍。 經過Polling(輪詢)Long-Polling(長輪詢)Websocketsse的對比。四種Web即時通訊技術比較它們的實現方式各自的優缺點。 對比優缺點以下:javascript

# 輪詢(Polling) 長輪詢(Long-Polling) Websocket sse
通訊協議 http http tcp http
觸發方式 client(客戶端) client(客戶端) client、server(客戶端、服務端) client、server(客戶端、服務端)
優勢 兼容性好容錯性強,實現簡單 比短輪詢節約資源 全雙工通信協議,性能開銷小、安全性高,可擴展性強 實現簡便,開發成本低
缺點 安全性差,佔較多的內存資源與請求數 安全性差,佔較多的內存資源與請求數 傳輸數據須要進行二次解析,增長開發成本及難度 只適用高級瀏覽器
延遲 非實時,延遲取決於請求間隔 同短輪詢 實時 非實時,默認3秒延遲,延遲可自定義

上面基本上包含了各個實現方式的優勢缺點,它們基於什麼協議、由那端主動發送數據。html

輪詢(Polling)

短輪詢(Polling)的實現思路就是瀏覽器端每隔幾秒鐘向服務器端發送http請求,服務端在收到請求後,不管是否有數據更新,都直接進行響應。在服務端響應完成,就會關閉這個Tcp鏈接,以下圖所示:前端

webSocket

示例代碼實現以下:java

function Polling() {
    fetch(url).then(data => {
        // somthing
    }).catch(err => {
        console.log(err);
    });
}
setInterval(polling, 5000);
複製代碼
  • 優勢:能夠看到實現很是簡單,它的兼容性也比較好的只要支持http協議就能夠用這種方式實現。
  • 缺點:可是它的缺點也很明顯就是很是的消耗資源,由於創建Tcp鏈接是很是消耗資源的,服務端響應完成就會關閉這個Tcp鏈接,下一次請求再次創建Tcp鏈接。

COMET

**Alex Russell(Dojo Toolkit 的項目 Lead)**稱這種基於HTTP長鏈接、無須在瀏覽器端安裝插件的「服務器推」技術爲「Comet」。 經常使用的COMET分爲兩種:基於HTTP的長輪詢(long-polling)技術,以及基於iframe的長鏈接流(stream)模式node

長輪詢(Long-Polling)

客戶端發送請求後服務器端不會當即返回數據,服務器端會阻塞請求鏈接不會當即斷開,直到服務器端有數據更新或者是鏈接超時才返回,客戶端纔再次發出請求新建鏈接、如此反覆從而獲取最新數據。大體效果以下:web

webSocket

客戶端的代碼以下:express

function LongPolling() {
    fetch(url).then(data => {
        LongPolling();
    }).catch(err => {
        LongPolling();
        console.log(err);
    });
}
LongPolling();
複製代碼
  • 優勢: 長輪詢和短輪詢比起來,明顯減小了不少沒必要要的http請求次數,相比之下節約了資源。
  • 缺點:鏈接掛起也會致使資源的浪費。

基於iframe的長鏈接流(stream)模式

當咱們在頁面中嵌入一個iframe並設置其src時,服務端就能夠經過長鏈接「源源不斷」地向客戶端輸出內容。 例如,咱們能夠向客戶端返回一段script標籤包裹的javascript代碼,該代碼就會在iframe中執行。所以,若是咱們預先在iframe的父頁面中定義一個處理函數process(),而在每次有新數據須要推送時,在該鏈接響應中寫入。那麼iframe中的這段代碼就會調用父頁面中預先定義的process()函數。(是否是有點像JSONP傳輸數據的方式?)後端

// 在父頁面中定義的數據處理方法
  function process(data) {
      // do something
  }

  // 建立不可見的iframe
  var iframe = document.createElement('iframe');
  iframe.style = 'display: none';
  // src指向後端接口
  iframe.src = '/long_iframe';
  document.body.appendChild(iframe);
複製代碼

後端仍是以node爲例瀏覽器

const app = http.createServer((req, res) => {
    // 返回數據的方法,將數據拼裝成script腳本返回給iframe
    const iframeSend = data => {
        let script = `<script type="text/javascript"> parent.process(${JSON.stringify(data)}) </script>`;
        res.write(script);
    };

    res.setHeader('connection', 'keep-alive');
    // 注意設置相應頭的content-type
    res.setHeader('content-type', 'text/html; charset=utf-8');        
    // 當有數據更新時,服務端「推送」數據給客戶端
    EVENT.addListener(MSG_POST, iframeSend);

    req.socket.on('close', () => {
        console.log('iframe socket close');
        // 注意在鏈接關閉時移除監聽,避免內存泄露
        EVENT.removeListener(MSG_POST, iframeSend);
    });
});

複製代碼

效果以下: 安全

webSocket

不過使用iframe有個小瑕疵,所以這個iframe至關於永遠也不會加載完成,因此瀏覽器上會一直有一個loading標誌。

他的優缺點和上面的長輪詢同樣。

Websocket

WebSocket的一些特性和基礎使用方法在這裏就很少贅述了,請看另外一篇博客webSocket(一) 淺析; 大體代碼以下: 服務端

const express = require('express');
    const app = express();
    const server = require('http').Server(app);
    const WebSocket = require('ws');

    const wss = new WebSocket.Server({port: 8080});
    wss.on('connection', function connection(ws) {
        console.log('server: receive connection');
        ws.on('message', function incoming(message) {
            console.log('server: recevied: %s', message);
        });
        ws.send('world');
    });

    app.get('/', function (req, res) {
        res.sendfile(__dirname + '/index.html');
    });
    app.listen(3000);
複製代碼

客戶端

const ws = new WebSocket('ws://localhost:8080');
    ws.onopen = function () {
        console.log('ws onopen');
        ws.send('from client:hello');
    };
    ws.onmessage = function (e) {
        console.log('ws onmessage');
        console.log('from server:' + e.data);
    }
複製代碼

運行效果以下:

webSocket

  • 優勢:不會形成性能的浪費
  • 缺點:學習一套新的請求庫

SSE (Server-Sent Events)

Server-SentHTML5提出一個標準。由客戶端發起與服務器之間建立TCP鏈接,而後並維持這個鏈接,直到客戶端或服務器中的任何一方斷開,ServerSent使用的是"問"+"答"的機制,鏈接建立後瀏覽器會週期性地發送消息至服務器詢問,是否有本身的消息。其實現原理相似於咱們在上一節中提到的基於iframe的長鏈接模式。 HTTP響應內容有一種特殊的content-type —— text/event-stream,該響應頭標識了響應內容爲事件流,客戶端不會關閉鏈接,而是等待服務端不斷得發送響應結果。 SSE規範比較簡單,主要分爲兩個部分:瀏覽器中的EventSource對象,以及服務器端與瀏覽器端之間的通信協議

基礎用法

在瀏覽器中能夠經過EventSource構造函數來建立該對象

var source = new EventSource('/sse');
複製代碼

SSE的響應內容能夠當作是一個事件流,由不一樣的事件所組成。這些事件會觸發前端EventSource對象上的方法。

// 默認的事件
  source.addEventListener('message', function (e) {
      console.log(e.data);
  }, false);

  // 用戶自定義的事件名
  source.addEventListener('my_msg', function (e) {
      process(e.data);
  }, false);

  // 監聽鏈接打開
  source.addEventListener('open', function (e) {
      console.log('open sse');
  }, false);

  // 監聽錯誤
  source.addEventListener('error', function (e) {
      console.log('error');
  });
複製代碼

EventSource經過事件監聽的方式來工做。注意上面的代碼監聽了y_msg事件,SSE支持自定義事件,默認事件經過監聽message來獲取數據。實現代碼以下:

客戶端

// 顯示聊天信息
  let chat = new EventSource("/chat-room");
  chat.onmessage = function (event) {
      let msg = event.data;
      $(".list-group").append("<li class='list-group-item'>" + msg + "</li>");
      // chat.close(); 關閉server-sent event
  };

  // 自定義事件
  chat.addEventListener("myChatEvent", function (event) {
      let msg = event.data;
      $(".list-group").append("<li class='list-group-item'>" + msg + "</li>");
  });
複製代碼

服務端 nodejs

var express = require('express');
  var router = express.Router();
  router.get('/chat-room', function (req, res, next) {
      // 當res.white的數據data 以\n\n結束時 就默認該次消息發送完成,觸發onmessage方法,以\r\n不會觸發onmessage方法
      res.header({
          "Content-Type": "text/event-stream",
          "Cache-Control": "no-cache",
          "Connection": "keep-alive"
      });

      // res.white("event: myChatEvent\r\n"); 自定義事件
      res.write("retry: 10000\r\n"); // 指定通訊的最大間隔時間
      res.write("data: start~~\n\n");
      res.end(); // 不加end不會認爲本次數據傳輸結束 會致使不會有下一次請求
  });
複製代碼
  • 優勢: 客戶端只需鏈接一次,Server就定時推送,除非其中一端斷開鏈接。而且SSE會在鏈接意外斷開時自動重連。
  • 缺點: 要學習新的語法

總結

上面四種Web即時通訊技術比較,能夠從不一樣的角度考慮,它們的優先級是不一樣的,基本上能夠分爲兩大類基於httptcp兩種通訊中的一種。

兼容性考慮短輪詢>長輪詢>長鏈接SSE>WebSocket

從性能方面考慮WebSocket>長鏈接SSE>長輪詢>短輪詢

服務端推送WebSocket>長鏈接SSE>長輪詢

參考

各種「服務器推」技術原理與實例(Polling/COMET/SSE/WebSocket)

JavaScript 服務器推送技術之 WebSocket

輪詢、長輪詢、長鏈接、websocket

消息推送機制-輪詢、長輪詢、SSE(Server Sent Event)和WS(WebSocket)

相關文章
相關標籤/搜索