做可視化,如大屏,往往需要數(shù)據(jù)實(shí)時(shí)更新,那么怎么實(shí)現(xiàn)呢?
- 下邊是簡單的思路:
- ajax短輪詢:每隔一段時(shí)間向服務(wù)端發(fā)送HTTP請求,服務(wù)器接收到請求后返回最新的數(shù)據(jù)!
- ajax長輪詢:與短輪詢類似,每隔一段時(shí)間向服務(wù)端發(fā)送HTTP請求,服務(wù)器接收到請求后,如果沒有更新的數(shù)據(jù),則阻塞程序,等到有數(shù)據(jù)更新時(shí)返回最新的數(shù)據(jù)!
- websocket:全雙工(雙向同時(shí)收發(fā)消息)通信,服務(wù)端有數(shù)據(jù)更新時(shí)可以主動推送到客戶端。
上邊說的只是簡單的實(shí)現(xiàn)方案,至于孰優(yōu)孰劣要從其各自背后的工作原理說起。說簡單點(diǎn),就是弄明白HTTP、WebSocket與服務(wù)器數(shù)據(jù)進(jìn)行通信過程的差異!
HTTP
之前的HTTP篇 講過,HTTP是無狀態(tài)的,為什么呢?因?yàn)镠TTP向服務(wù)器請求數(shù)據(jù)時(shí),先要通過TCP與服務(wù)器建立連接(三次握手),發(fā)送請求,服務(wù)器解析請求并返回相應(yīng)數(shù)據(jù),連接關(guān)閉(清除身份驗(yàn)證信息),完成一次請求過程。下次請求重新建立連接,重新驗(yàn)證身份。這樣一來,ajax輪詢的方式,需要不停的發(fā)送請求,每次都要重新握手,效率比較低下。
另外,HTTP請求是被動的,即客戶端發(fā)送request,服務(wù)端才會返回response。
WebSocket
2008年誕生的websocket,如今已被各大瀏覽器支持,與HTTP大致屬于求同存異的關(guān)系吧!WebSocket在向服務(wù)端請求數(shù)據(jù)是也是需要與服務(wù)端建立連接的(而且是借助HTTP建立連接的方式實(shí)現(xiàn)的,只不過在建立連接后通過‘Upgrade: websocket’的請求頭設(shè)置,將協(xié)議由HTTP轉(zhuǎn)換為websocket,之后的數(shù)據(jù)通信便于HTTP再無關(guān)系),只不過成功建立連接后,在數(shù)據(jù)相應(yīng)之后沒有立即關(guān)閉(長連接),而是以阻塞的形式繼續(xù)等待請求(因?yàn)閰f(xié)議變成了websocket),之后所有數(shù)據(jù)通信均由該連接完成。而且該連接是全雙工的,支持客戶端和服務(wù)端同時(shí)收發(fā)消息!
比較
通過上面的簡單介紹,相信你對HTTP與WebSocket在數(shù)據(jù)通信方面已經(jīng)有了一些認(rèn)識,以及對數(shù)據(jù)實(shí)時(shí)更新問題解決的優(yōu)劣也有了判斷。
- ajax輪詢:需要重復(fù)建立連接,資源消耗大,效率低下;被動更新數(shù)據(jù)。
- websocket:一次連接,終身受益;主被動兼顧!
實(shí)現(xiàn)
參考MDN-Git(JavaScript實(shí)現(xiàn)樣例),貼出主要代碼:
- client.js
function connect() {
// 以ws(s)開頭的URL
var serverUrl = "ws://" + window.location.hostname + ":6502";
// 創(chuàng)建連接
connection = new WebSocket(serverUrl);
connection.onopen = function(evt) {
// 成功建立連接
};
connection.onmessage = function(evt) {
// 從服務(wù)端接受到的消息
var msg = JSON.parse(evt.data);
//通過判斷消息type,執(zhí)行相關(guān)操作
switch(msg.type) {
case "id":
// ...
break;
case "username":
// ...
break;
case "message":
// ...
break;
}
}
- server.js
var http = require("http");
var url = require("url");
var fs = require("fs");
var WebSocketServer = require("websocket").server;
var connectionArray = [];
var nextID = Date.now();
var appendToMakeUnique = 1;
const dirname = "./websocket-chat";
var server = http.createServer(function(request, response) {
console.log(new Date() + " Received request for " + request.url);
if (request.url === "/") {
//設(shè)置編碼
response.setHeader("Content-Type", "text/html;charset=utf-8");
fs.createReadStream(dirname + "/index.html").pipe(response);
} else {
if (fs.existsSync(`.${request.url}`)) {
fs.createReadStream(`.${request.url}`).pipe(response);
} else {
response.statusCode = 404;
response.end();
}
}
});
// 發(fā)布服務(wù),監(jiān)聽端口6502
server.listen(6502, function() {
console.log(new Date() + " Server is listening on port 6502");
});
// Create the WebSocket server
var wsServer = new WebSocketServer({
httpServer: server,
autoAcceptConnections: true // You should use false here!
});
wsServer.on("connect", function(connection) {
// ...
// Handle the "message" event received over WebSocket. This
// is a message sent by a client, and may be text to share with
// other users or a command to the server.
connection.on("message", function(message) {
if (message.type === "utf8") {
// Process messages
var sendToClients = true;
msg = JSON.parse(message.utf8Data);
var connect = getConnectionForID(msg.id);
switch (msg.type) {
case "message":
// ...
break;
case "username":
// ...
break;
}
});
// Handle the WebSocket "close" event; this means a user has logged off
// or has been disconnected.
connection.on("close", function(connection) {
// ...
});
參考
時(shí)間和篇幅有限,不能完全解釋清楚,感興趣的可以討論或者參看參考列出的內(nèi)容
- TCP/IP協(xié)議,HTTP協(xié)議與webSocket協(xié)議區(qū)別
- 刨根問底HTTP和WebSocket協(xié)議(二)
- TCP、UDP、HTTP、SOCKET、WebSocket之間的區(qū)別
- MDN-WebSocket API
如果您感覺有所幫助,或者有問題需要交流,歡迎留言評論,非常感謝!
前端菜鳥,還請多多關(guān)照!