數據實時更新--webSocket/輪詢


一. 基本概念

   單工:單向通信。即只能服務器->客戶端。例如: UDP協議

半雙工:既可以服務器->客戶端,也可以客戶端->服務器。但是同一時間,只能是一個方向。例如: http協議。

全雙工:雙向通信。同一時間內既可以客戶端->服務器;也可以服務器->客戶端。例如:webSocket協議

二. 雙向通信

服務器實時的將更新的數據發送到客戶端。
應用:
1)即時聊天工具
2)股票等實時信息展示工具
 
有以下 實現方式

1. 輪詢

輪詢即周期性(setInterval)的發起請求並返回響應(發送請求->響應數據->關閉連接->發送請求)。

如果響應的所需時間過長,它的再次請求也不會等待上一次的響應完成。

缺點:

1)請求過於頻繁,請求數過多,每次請求都要攜帶請求頭,浪費流量,消耗CPU的利用率

2)當客戶端過多時,會導致並發數量過高

3)后台數據不頻繁變化時, 頻繁發出的很多請求是無效請求

 

 

示例:

客戶端:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="root"></div>
  <script>
      const xhr = new XMLHttpRequest();
      setInterval(function() {
        xhr.open('GET', '/clock', true);
        xhr.onreadystatechange = function() {
          if(xhr.readyState === 4 && xhr.status === 200) {
            window.root.innerHTML = xhr.response;
          }
        }
        xhr.send();
      }, 1000);
  </script>
</body>
</html>

服務器:

const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/clock', function(req, res) {
  res.send(new Date().toLocaleString());
});
app.listen(8888);

2. 長輪詢(Long Polling)

長輪詢是輪詢的優化。它會等待上一次的響應完成后(返回需要的數據)或者請求超時后,再發起下一次的請求。

優點:

如果響應時間大於輪詢的周期頻率,會減少請求次數,實現節約流量(請求header/請求次數),提高CPU利用率的作用。

示例:

客戶端:

<body>
  <div id="root"></div>
  <script>
      const xhr = new XMLHttpRequest();
      // 自執行函數
      (function longPolling() {
        xhr.open('GET', '/clock', true);
        xhr.onreadystatechange = function() {
          if(xhr.readyState === 4 && xhr.status === 200) {
            // 等響應返回后再次發起請求
            window.root.innerHTML = xhr.response;
            longPolling();
          }
        }
        xhr.send();
      })();
  </script>
</body>

服務器:

const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/clock', function(req, res) {
  // 模擬響應時間較長的情況
  setTimeout(function() {
    res.send(new Date().toLocaleString());
  }, 3000);
});
app.listen(8888);

3. iframe流

本質上是使用“長連接”,即請求發送后,定時返回響應,並不調用(res.end)斷開連接。

在頁面中嵌套一個隱藏的iframe,其src設置為一個長鏈接請求,服務器源源不斷的向客戶端推送數據。

 

 

示例:

客戶端

<body>
  <div id="root"></div>
  <iframe src='/clock' style="display: none"></iframe>
</body>

服務器

const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/clock', function(req, res) {
  setInterval(function() {
    // 定時調用res.write/不調用res.end;即不斷開連接
    // 時間必須用雙引號包裹,否則不會將其識別為字符串
    res.write(`
      <script>
        parent.document.getElementById('root').innerHTML = "${new Date().toLocaleString()}"
      </script>
    `);
  }, 1000)
});
app.listen(8888);

 4. webSocket

WebSocket是H5提供的一種新的通信方式,可以使客戶端和服務器保持持久連接。

WebSocket使用ws協議,和http協議是同級協議,都是應用層協議。而TCP協議是傳輸層協議。

WebSocket基於TCP協議,可以基於TCP實現webSocket服務器。並且復用http協議的握手通道。

優點:

1)全雙工通信

2)請求頭體積小

缺點:

1)不兼容

2)客戶端和瀏覽器端用法不同

示例:

客戶端

<body>
  用戶:<input id="username" />
  密碼:<input id="password" type="password" />
  <br/>
  <button onclick="login()">登錄</button>
  <script>
    const ws = new WebSocket('ws://localhost:8888');
    ws.onopen = function() {
      console.log('連接成功');
    };
    ws.onmessage = function(event) {// 監聽響應事件
      console.log('接受到服務器響應',event.data);
    };
    // 登錄函數
    function login() {
      const username = document.getElementById('username').value;
      const password = document.getElementById('password').value;
      // 客戶端發送登錄信息
      ws.send(JSON.stringify({type: 'login', username, password}));
    }
  </script>
</body>

服務器

// 為靜態資源文件起服務;http協議
const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/clock', function(req, res) {
  res.send(new Date().toLocaleString());
});
app.listen(8080);

// ws協議;webSocket通信的服務器
const WebSocketServer = require('ws').Server;
const { sign } = require('jsonwebtoken');// 使用JWT

const server = new WebSocketServer({port: 8888});
server.on('connection', function(socket) {
  console.log('連接成功');
  socket.on('message', function(message) {
    console.log('服務器接收到消息:',message);
    const {username, password, type} = JSON.parse(message);
    if(type === 'login') {
      // 同步獲取token; sign的參數依次是data,secret,alg,callback(異步獲取token)
      const token = sign({username, password}, 'lyra');// 默認Hmac SHA256
      socket.send(JSON.stringify({type: 'logined', token}));
    }
  })
});

5. socket.io

socket.io是一個webSocket庫,它包含瀏覽器端的js和服務器端的nodeJS, 目的是構建在不同瀏覽器和設備上的、通用的網絡實時應用。

優點:

1. 易用性:它封裝了客戶端和服務端

2. 跨平台

3. 自適應: 可以根據瀏覽器自動從webSocket/長輪詢/iframe流中選擇

基礎用法:

// 1. 全局廣播
io.emit('message', content);
// 2. 向除自己之外的所有人廣播
socket.broadcast.emit('message', content);
// 3.只廣播到特定人(socket對應的客戶端用戶)
socket.emit('message', content);
// 或者
socket.send(content);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM