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 )