数据实时更新--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);

 

posted @ 2020-02-17 21:30  Lyra李  阅读(1376)  评论(0编辑  收藏  举报