双向通信之Comet

430 阅读2分钟

Comet是一种用于web的推送技术,能使服务器实时地将更新的信息传送到客户端,而无须客户端发出请求,当前有三种实现方式,轮询 长轮询和iframe流。

轮询

客户端和服务器之间一直进行连接,每隔一段时间就询问一次

特点: 这种方式连接数会很多,一个接受,一个发送。而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CPU的利用率

<body>
    <div id="clock"></div>
    <script>
        let clock = document.getElementById('clock');
        setInterval(function(){
            let xhr = new XMLHttpRequest;
            xhr.open('GET', '/clock', true);
            xhr.onreadystatechange = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    clock.innerHTML = xhr.responseText;
                }
            }
            xhr.send();
        }, 1000);  
    </script>
</body>

server.js

let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.get('/clock', (req,res) => {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.end(new Date().toLocaleTimeString());
});
app.listen(8000);

长轮询

在打开一条连接以后保持,等待服务器推送来数据再关闭的方式。

特点: 这种方式在某种程度上减小了网络带宽和CPU利用率等问题。但由于http数据包的头部数据量往往很大(通常有400多个字节),真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费

<div id="clock"></div>
    <script>
        (function poll() {
                let xhr = new XMLHttpRequest();
                xhr.open('GET', 'http://localhost:8080', true);
                xhr.onreadystatechange = function () {
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        document.querySelector('#clock').innerHTML = xhr.responseText;
                        poll();
                    }
                }
                xhr.send();
        })();
    </script>

iframe流

在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长链接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。

特点: 浏览器兼容好

<div id="clock"></div>
<iframe src="http://localhost:8000/clock" frameborder="0" />
<script>
    function setTime(ts) {
        document.querySelector("#clock").innerHTML = ts;
    }
</script>

server.js

const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/clock', function (req, res) {
    res.header('Content-type', 'text/html');
    setInterval(function () {
        res.write(`<script>
        parent.setTime('${new Date().toLocalString()}'); // 如果在子窗口调用父窗口的方法,通过parent(为window对象)
    </script>`); // 如果使用res.send()则返回数据给客户端同时关闭连接
    }, 1000);
});
app.listen(8000);