以下都是拾人牙慧作為參考學習總結
傳統輪詢(Traditional Polling)
當前Web應用中較常見的一種持續通訊方式,通常採取 setInterval 或者 setTimeout 實現。例如如果我們想要定時獲取並重新整理頁面上的資料,可以結合Ajax寫出如下實現:
setInterval(function() {
$.get("/path/to/server", function(data, status) {
console.log(data);
});
}, 10000);複製程式碼
上面的程式會每隔10秒向伺服器請求一次資料,並在資料到達後儲存。這個實現方法通常可以滿足簡單的需求,然而同時也存在著很大的缺陷:在網路情況不穩定的情況下,伺服器從接收請求、傳送請求到客戶端接收請求的總時間有可能超過10秒,而請求是以10秒間隔傳送的,這樣會導致接收的資料到達先後順序與傳送順序不一致。於是出現了採用 setTimeout 的輪詢方式:
function poll() {
setTimeout(function() {
$.get("/path/to/server", function(data, status) {
console.log(data);
// 發起下一次請求
poll();
});
}, 10000);
}複製程式碼
程式首先設定10秒後發起請求,當資料返回後再隔10秒發起第二次請求,以此類推。這樣的話雖然無法保證兩次請求之間的時間間隔為固定值,但是可以保證到達資料的順序。
缺陷
程式在每次請求時都會新建一個HTTP請求,然而並不是每次都能返回所需的新資料。當同時發起的請求達到一定數目時,會對伺服器造成較大負擔。
長輪詢(long poll)
客戶端傳送一個request後,伺服器拿到這個連線,如果有訊息,才返回response給客戶端。沒有訊息,就一直不返回response。之後客戶端再次傳送request, 重複上次的動作。
總結
http協議的特點是伺服器不能主動聯絡客戶端,只能由客戶端發起。它的被動性預示了在完成雙向通訊時需要不停的連線或連線一直開啟,這就需要伺服器快速的處理速度或高併發的能力,是非常消耗資源的。
什麼是websocket?
websocket是HTML5的一個新協議,它允許服務端向客戶端傳遞資訊,實現瀏覽器和客戶端雙工通訊
故事
因為 HTTP 協議有一個缺陷:通訊只能由客戶端發起。 舉例來說,我們想了解今天的天氣,只能是客戶端向伺服器發出請求,伺服器返回查詢結果。HTTP 協議做不到伺服器主動向客戶端推送資訊。這種單向請求的特點,註定瞭如果伺服器有連續的狀態變化,客戶端要獲知就非常麻煩。我們只能使用"輪詢":每隔一段時候,就發出一個詢問,輪詢的效率低,非常浪費資源(因為必須不停連線,或者 HTTP 連線始終開啟)。因此,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。
websocket的特點
伺服器可以主動向客戶端推送資訊,客戶端也可以主動向伺服器傳送資訊,是真正的雙向平等對話,屬於伺服器推送技術的一種。
- 與 HTTP 協議有著良好的相容性。預設埠也是 80 和 443 ,並且握手階段採用 HTTP 協議,因此握手時不容易遮蔽,能通過各種 HTTP 代理伺服器。
- 建立在TCP協議基礎之上,和http協議同屬於應用層
- 資料格式比較輕量,效能開銷小,通訊高效。
- 可以傳送文字,也可以傳送二進位制資料。
- 沒有同源限制,客戶端可以與任意伺服器通訊
- 協議識別符號是ws(如果加密,則為wss),伺服器網址就是 URL,如ws://localhost:8023
跨平臺的WebSocket通訊庫socket.io
跨平臺的WebSocket通訊庫,具有前後端一致的API,可以觸發和響應自定義的事件。socket.io最核心的兩個api就是emit 和 on了 ,服務端和客戶端都有這兩個api。通過 emit 和 on可以實現伺服器與客戶端之間的雙向通訊。
- emit :發射一個事件,第一個引數為事件名,第二個引數為要傳送的資料,第三個引數為回撥函式(如需對方接受到資訊後立即得到確認時,則需要用到回撥函式)。
- on :監聽一個 emit 發射的事件,第一個引數為要監聽的事件名,第二個引數為回撥函式,用來接收對方發來的資料,該函式的第一個引數為接收的資料。
服務端
var app = require('express')();
var http = require('http');
var socketio = require("socket.io");
const server = http.createServer(app)
const io = socketio(server)
var count = 0;
// WebSocket 連線伺服器
io.on('connection', (socket)=> {
//// 所有的事件觸發響應都寫在這裡
setInterval(()=>{
count++
//向建立該連線的客戶端傳送訊息
socket.emit('mynameEv', { name:"你我貸"+count})
},1000)
//監聽客戶端傳送資訊
socket.on('yournameEv', function (data) {
console.log(data)
})
})
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// 啟用3000埠
server.listen(3000)複製程式碼
客戶端
<body>
<div id="myname"></div>
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script>
var count = 0;
const socket = io.connect('http://localhost:3000')
socket.on('mynameEv', (data)=>{
document.getElementById("myname").innerHTML = data.name;
console.log(data.name)
setInterval(()=>{
count++
socket.emit('yournameEv', { name:"飛旋"+count})
},1000)
})
</script>
</body>複製程式碼