Web 实时推送技术

1、轮询(Polling)

  轮询是由客户端每隔一段时间向服务器端发起请求,查看服务器端是否产生新的数据。
优点:实现简单,只需在原有代码中添加定时器即可完成。
缺点:轮询时间间隔不好设计,过长过短都不好。过长,导致用户不能及时接收到更新的数据;过短,导致查询请求过多,增加服务器的负担。并且,连接数会很多,每次请求都会产生 HTTP 的 header,有效负载过低。

2、长轮询(Long-Polling)

  长轮询是对轮询的改进版,当服务器收到客户端发来的查询请求时,如果没有新数据就会阻塞请求,直到有新数据产生时才返回。当服务器响应或者连接超时后,客户端再次发起请求。
优点:对 Polling 做了优化,有很好的时效性,客户端代码无需修改。
缺点:保持连接会消耗资源;会有连接超时的情况。

3、长连接(基于iframe)

  iframe 流的方式是在页面中插入一个隐藏的 iframe,利用其src属性在服务器和客户端创建一个长连接,服务器向iframe源源不断的传输数据,来实时更新页面。
优点:浏览器兼容性好;消息能实时到达;
缺点:服务器维护一个长连接会增加开销;IE、Firefox会显示加载没有完成,图标会不停旋转。

4、webSocket

  WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器端的数据交换变得简单,其允许服务端主动像客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 的特点:
  • 支持双向通信,实时性更强
  • 可以发送文本,也可以发送二进制数据
  • 减少通信量:只要建立起WebSocket 连接,就希望一直保持连接状态。

5、Demo

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <div>
        <label>轮询:</label>
        <span class="polling"></span>
    </div>
    <div>
        <label>长轮询:</label>
        <span class="long-polling"></span>
    </div>
    <div>
        <label>长连接(iframe):</label>
        <span class="iframeText"></span>
    </div>
    <div>
        <label>WebSocket:</label>
        <span class="websocketText"></span>
    </div>
    <iframe src="/iframeStream?callback=parent.iframeCallback" style="display:none;"></iframe>
    
    <script>
        let pollingText = document.getElementsByClassName('polling')[0];
        let longPollingText = document.getElementsByClassName('long-polling')[0];
        let iframeText = document.getElementsByClassName('iframeText')[0];
        let websocketText = document.getElementsByClassName('websocketText')[0];

        let intervalTime = setInterval(function pollingFun(){
            let xhr = new XMLHttpRequest();
            xhr.open('GET', '/polling', true);
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4 && xhr.status == 200){
                    pollingText.innerText = xhr.responseText;
                }
            }
            xhr.send();
        },1000);

        function longPolling(){
            let xhr = new XMLHttpRequest();
            xhr.open('GET', '/longpolling', true);
            xhr.timeout = 2000; // 超时
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    if(xhr.status == 200){
                        longPollingText.innerText = xhr.responseText;
                    }
                    longPolling();
                }
            }
            xhr.ontimeout = function(){
                longPolling();
            }
            xhr.send();
        }
        longPolling();

        function iframeCallback(res){
            iframeText.innerText = res;
        }

        let socket = new WebSocket('ws://localhost:8080');
        socket.onopen = function(){
            console.log('客户端连接成功')
            socket.send('hello');
        }
        socket.onmessage = function(event){
            websocketText.innerText = event.data;
            console.log('收到服务端消息:', event.data);
            
        }

    </script>
    
</body>
</html>
server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');

const basePath = path.join(__dirname, '/');
const server = http.createServer((req, res) => {
    if(req.url === '/longpolling'){
        // setTimeout(() => {res.end(new Date().toLocaleString())}, 10000);
        res.end(new Date().toLocaleString());
    } else if(req.url === '/'){
        fs.readFile(basePath + 'index.html',(err, data) => {
            if(err && err.code !== 'ENOENT'){
                throw err;
            }
            res.end(data);
        })

    } else if(req.url === '/polling'){
        res.end(new Date().toLocaleString());
    } else if(req.url.startsWith('/iframeStream')){
        let urlObj = url.parse(req.url, true);
        setInterval(() => {
            res.write(`<script>${urlObj.query.callback}('${new Date().toLocaleString()}')</script>`)
        }, 1000)
    }
});

server.listen(8000);
WebSocket
const WebSocketServer = require('ws').Server;  // npm install ws

let wss = new WebSocketServer({port:8080});

let socket;

wss.on('connection', function(ws){
    socket = ws;
    ws.on('message', function(message){
        socket.send(new Date().toLocaleString());
        setInterval(() => {
            socket.send(new Date().toLocaleString());
        }, 1000)
    })
    ws.on('close', (message) =>{})
})

参考:Web 实时推送技术的总结(https://mp.weixin.qq.com/s/fnRAqxA1JCWppFGBAHwreA

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值