Web 實時推送技術的總結

      隨着 Web 的發展,用戶對於 Web 的實時推送要求也越來越高 ,比如,工業運行監控、Web 在線通訊、即時報價系統、在線遊戲等,都需要將後臺發生的變化主動地、實時地傳送到瀏覽器端,而不需要用戶手動地刷新頁面。本文對過去和現在流行的 Web 實時推送技術進行了比較與總結。

一、雙向通道

HTTP 協議有一個缺陷:通信只能由客戶端發起。舉例來說,我們想了解今天的天氣,只能是客戶端向服務器發出請求,服務器返回查詢結果。HTTP 協議做不到服務器主動向客戶端推送信息。這種單向請求的特點,註定瞭如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。在WebSocket協議之前,有三種實現雙向通信的方式:輪詢(polling)、長輪詢(long-polling)和iframe流(streaming)

1.輪詢(polling)

http輪詢消息響應實例
Http輪詢消息機制

 

 

 

 

 

 

 

 

 

 

 

     輪詢是客戶端和服務器之間會一直進行連接,每隔一段時間就詢問一次。其缺點也很明顯:連接數會很多,一個接受,一個發送。而且每次發送請求都會有Http的Header,會很耗流量,也會消耗CPU的利用率。    

  • 優點:實現簡單,無需做過多的更改

  • 缺點:輪詢的間隔過長,會導致用戶不能及時接收到更新的數據;輪詢的間隔過短,會導致查詢請求過多,增加服務器端的負擔

<div id="clock"></div>
    <script>
        let clockDiv = document.getElementById('clock');
        setInterval(function(){
            let xhr = new XMLHttpRequest;
            xhr.open('GET','/clock',true);
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4 && xhr.status == 200){
                    console.log(xhr.responseText);
                    clockDiv.innerHTML = xhr.responseText;
                }
            }
            xhr.send();
        },1000);
    </script>
//輪詢 服務端
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.get('/clock',function(req,res){
  res.end(new Date().toLocaleString());
});
app.listen(8080);

啓動本地服務,打開http://localhost:8080/1.html,得到如下結果:

本地運行結果

2、長輪詢(long-polling)

   長輪詢資源

長輪詢是對輪詢的改進版,客戶端發送HTTP給服務器之後,看有沒有新消息,如果沒有新消息,就一直等待。當有新消息的時候,纔會返回給客戶端。在某種程度上減小了網絡帶寬和CPU利用率等問題。由於http數據包的頭部數據量往往很大(通常有400多個字節),但是真正被服務器需要的數據卻很少(有時只有10個字節左右),這樣的數據包在網絡上週期性的傳輸,難免對網絡帶寬是一種浪費

  • 優點:比 Polling 做了優化,有較好的時效性

  • 缺點:保持連接會消耗資源; 服務器沒有返回有效數據,程序超時。

2.html 服務端代碼同上
    <div id="clock"></div>
    <script>
    let clockDiv = document.getElementById('clock')
    function send() {
      let xhr = new XMLHttpRequest()
      xhr.open('GET', '/clock', true)
      xhr.timeout = 2000 // 超時時間,單位是毫秒
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if (xhr.status == 200) {
            //如果返回成功了,則顯示結果
            clockDiv.innerHTML = xhr.responseText
          }
          send() //不管成功還是失敗都會發下一次請求
        }
      }
      xhr.ontimeout = function() {
        send()
      }
      xhr.send()
    }
    send()
    </script>

3、iframe流(streaming)

ifream流消息

iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間創建一條長連接,服務器向iframe傳輸數據(通常是HTML,內有負責插入信息的javascript),來實時更新頁面。

  • 優點:消息能夠實時到達;瀏覽器兼容好

  • 缺點:服務器維護一個長連接會增加開銷;IE、chrome、Firefox會顯示加載沒有完成,圖標會不停旋轉。

3.html
    <body>
        <div id="clock"></div>
        <iframe src="/clock" style="display:none"></iframe>
    </body>
//iframe流
let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock', function(req, res) {
  setInterval(function() {
    let date = new Date().toLocaleString()
    res.write(`
       <script type="text/javascript">
         parent.document.getElementById('clock').innerHTML = "${date}";//改變父窗口dom元素
       </script>
     `)
  }, 1000)
})
app.listen(8080)

啓動本地服務,打開http://localhost:8080/3.html,得到如下結果:

上述代碼中,客戶端只請求一次,然而服務端卻是源源不斷向客戶端發送數據,這樣服務器維護一個長連接會增加開銷。

以上我們介紹了三種實時推送技術,然而各自的缺點很明顯,使用起來並不理想,接下來我們着重介紹另一種技術--websocket,它是比較理想的雙向通信技術。

二、WebSocket

1、什麼是websocket

WebSocket是一種全新的協議,隨着HTML5草案的不斷完善,越來越多的現代瀏覽器開始全面支持WebSocket技術了,它將TCP的Socket(套接字)應用在了webpage上,從而使通信雙方建立起一個保持在活動狀態連接通道。

一旦Web服務器與客戶端之間建立起WebSocket協議的通信連接,之後所有的通信都依靠這個專用協議進行。通信過程中可互相發送JSON、XML、HTML或圖片等任意格式的數據。由於是建立在HTTP基礎上的協議,因此連接的發起方仍是客戶端,而一旦確立WebSocket通信連接,不論服務器還是客戶端,任意一方都可直接向對方發送報文

初次接觸 WebSocket 的人,都會問同樣的問題:我們已經有了 HTTP 協議,爲什麼還需要另一個協議?

2、HTTP的侷限性

  • HTTP是半雙工協議,也就是說,在同一時刻數據只能單向流動,客戶端向服務器發送請求(單向的),然後服務器響應請求(單向的)。

  • 服務器不能主動推送數據給瀏覽器。這就會導致一些高級功能難以實現,諸如聊天室場景就沒法實現。

3、WebSocket的特點

  • 支持雙向通信,實時性更強

  • 可以發送文本,也可以發送二進制數據

  • 減少通信量:只要建立起WebSocket連接,就希望一直保持連接狀態。和HTTP相比,不但每次連接時的總開銷減少,而且由於WebSocket的首部信息很小,通信量也相應減少了

相對於傳統的HTTP每次請求-應答都需要客戶端與服務端建立連接的模式,WebSocket是類似Socket的TCP長連接的通訊模式,一旦WebSocket連接建立後,後續數據都以幀序列的形式傳輸。在客戶端斷開WebSocket連接或Server端斷掉連接前,不需要客戶端和服務端重新發起連接請求。在海量併發和客戶端與服務器交互負載流量大的情況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優勢,且客戶端發送和接受消息是在同一個持久連接上發起,實時性優勢明顯

接下來我看下websocket如何實現客戶端與服務端雙向通信:

// websocket.html
    <div id="clock"></div>
    <script>
    let clockDiv = document.getElementById('clock')
    let socket = new WebSocket('ws://localhost:9999')
    //當連接成功之後就會執行回調函數
    socket.onopen = function() {
      console.log('客戶端連接成功')
      //再向服務 器發送一個消息
      socket.send('hello') //客戶端發的消息內容 爲hello
    }
    //綁定事件是用加屬性的方式
    socket.onmessage = function(event) {
      clockDiv.innerHTML = event.data
      console.log('收到服務器端的響應', event.data)
    }
    </script>
// websocket.js
let express = require('express')
let app = express()
app.use(express.static(__dirname))
//http服務器
app.listen(3000)
let WebSocketServer = require('ws').Server
//用ws模塊啓動一個websocket服務器,監聽了9999端口
let wsServer = new WebSocketServer({ port: 9999 })
//監聽客戶端的連接請求 當客戶端連接服務器的時候,就會觸發connection事件
//socket代表一個客戶端,不是所有客戶端共享的,而是每個客戶端都有一個socket
wsServer.on('connection', function(socket) {
  //每一個socket都有一個唯一的ID屬性
  console.log(socket)
  console.log('客戶端連接成功')
  //監聽對方發過來的消息
  socket.on('message', function(message) {
    console.log('接收到客戶端的消息', message)
    socket.send('服務器迴應:' + message)
  })
})

啓動本地服務,打開http://localhost:3000/websocket.html,得到如下結果:

三、Web 實時推送技術的比較

方式 類型 技術實現 優點 缺點 適用場景
輪詢Polling client→server 客戶端循環請求

1、實現簡單

2、 支持跨域

1、浪費帶寬和服務器資源 2、 一次請求信息大半是無用(完整http頭信息) 3、有延遲 4、大部分無效請求 適於小型應用
長輪詢Long-Polling client→server 服務器hold住連接,一直到有數據或者超時才返回,減少重複請求次數 1、實現簡單 2、不會頻繁發請求 3、節省流量 4、延遲低 1、服務器hold住連接,會消耗資源 2、一次請求信息大半是無用 WebQQ、Hi網頁版、Facebook IM
長連接iframe client→server 在頁面裏嵌入一個隱蔵iframe,將這個 iframe 的 src 屬性設爲對一個長連接的請求,服務器端就能源源不斷地往客戶端輸入數據。 1、數據實時送達 2、不發無用請求,一次鏈接,多次“推送” 1、服務器增加開銷 2、無法準確知道連接狀態 3、IE、chrome等一直會處於loading狀態 Gmail聊天
WebSocket server⇌client new WebSocket() 1、支持雙向通信,實時性更強 2、可發送二進制文件3、減少通信量 1、瀏覽器支持程度不一致 2、不支持斷開重連
網絡遊戲、銀行交互和支付

 

 

 

 

 

 

 

 

 

 

 

 

 

 

綜上所述:Websocket協議不僅解決了HTTP協議中服務端的被動性,即通信只能由客戶端發起,也解決了數據同步有延遲的問題,同時還帶來了明顯的性能優勢,所以websocket 是Web 實時推送技術的比較理想的方案,但如果要兼容低版本瀏覽器,可以考慮用輪詢來實現。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章