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 )