今天沒有延續上一篇講的內容,穿插一段小插曲,WebSocket 實時資料通訊同步的問題,今天我們並不是很純粹地講 WebSocket 相關知識,我們通過 WebGL 3D 拓撲圖來呈現一個有趣的 Demo。接下來我們就看看這個實時資料通訊是一個什麼樣的套路。
我們先來聊聊這次 Demo 的思路吧,首先我要有一個 3D 的拓撲圖元件,在上面建立幾個節點,然後通過拉力佈局(ForceLayout)將這些節點自動佈局,但是有一定,需要在不同的網頁視窗下,對應節點的位置是一樣的,簡單地說就是不同網頁視窗所呈現的節點佈局是一樣,而且拖動不同網頁視窗中的任意的節點,都將更新所有頁面視窗,讓所有視窗的呈現都是一樣的。
根據上面的思路,我們該如何去規劃呢?既然需要實時資料通訊,那麼就需要使用 WebSocket,WebSocket 又是什麼呢?WebSocket 是 HTML5 一種新的協議,它沒有標準的 API,各個實現都有自己的一套 API,在這裡我們就不去詳細研究 WebSocket 的具體實現,我也講不了,至少現在講不了。
在這裡我們用比較易上手的 Node.js 的 Socket.IO 做通訊框架,Socket.IO 讓長連線通訊變得無比簡單,伺服器再也不用等待客戶端的請求就可以直接給客戶端傳送訊息,根據這樣的特性就可以實現資料通訊同步的問題。
我們來寫一個最簡單的例子,將任何一個客戶端傳送到伺服器的訊息,原封不動的轉發到所有連線到伺服器的客戶端,我們來看看要實現這樣的一個功能,服務端要怎麼設計。
首先我們得搭建一個簡易的 web 伺服器。
var app = require('express')(); var http = require('http').Server(app); app.get('/', function(req, res) { res.end('<h1>Hello Message!</h1>'); }); http.listen(4000, function() { console.log('listening on *:4000'); });
以上的程式碼的 Node.js 的程式碼,將這串程式碼貼到一個 js 檔案中,比如命名為 server.js 然後在 Terminal 中 cd 到 server.js 對應的資料夾下,如果 node server.js 後回車,如果發現報了 Cannot find module ‘xxx’ 的字樣,那麼說明你在當前目錄下沒有安裝程式用到的相關包。那麼我們在當前目錄下建立一個叫 package.json 的檔案,然後把下面的程式拷貝到該檔案中,然後在 Terminal 中輸入 npm install,等安裝完後,就可以正常啟動伺服器了。
{ "name": "socket-example", "version": "0.0.1", "description": "my first socket.io app", "dependencies": { "express": "^4.10.2", "socket.io": "^1.4.8" } }
啟動後,你在瀏覽器上輸入 localhost:4000 就可以看到 Hello Message! 的字樣。這是最簡單的 HTTP 伺服器,那麼我們如何在上面加上 WebSocket 的功能呢呢?眼尖的同學可能已經發現上面的 package.json 的內容已經包含了 Socket.IO,那麼 Socket.IO 要怎麼用呢,怎麼樣才能達到實時資料通訊的效果呢?
var io = require('socket.io')(http); io.on('connection', function(socket) { console.log('a user connected'); socket.on('disconnect', function() { console.log('user disconnected'); }); socket.on('message', function(msg) { io.emit('message', msg); }); });
在 server.js 中加入上面那串程式碼,就可以實現客戶端之間的實時資料通訊問題。但是在瀏覽器輸入 localhost:4000 你看到的是 Hello Message! 的字樣,要怎樣才能訪問到具體的 html 網頁內容呢?這個時候就需要稍微修改下我們的伺服器了。
app.get('/', function(req, res) { res.sendFile(__dirname + '/index.html'); });
也就是將前面提到的 res.end('<h1>Hello Message!</h1>’); 程式碼換成 res.sendFile(__dirname + ‘/index.html'); 做個頁面跳轉,從而達到訪問具體 html 網頁的目的,在這邊是是為了滿足 Demo 而做的方案,要搭建一個真正的 http 靜態伺服器肯定不是這樣子的,搭建 http 靜態伺服器我在這邊就不介入研究了,大家剛興趣的話,可以自己到網上搜尋學習。
那麼客戶端該如何實現來展現服務端的實時通訊呢?
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Socket.IO Message</title> <style media="screen"> #send { font-size: 14px; } #msgList { list-style-type: none; margin: 10px 0px; padding: 0; } #msgList li { padding: 5px 10px; } #msgList li:nth-child(odd) { background: #eee; } </style> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); var init = function() { var input = document.getElementById('message'), sendFunc = function() { var msg = input.value; if (!msg) return; socket.emit('message', input.value); input.value = ''; }; input.addEventListener('keyup', function(e) { if (e.keyCode === 13) { sendFunc(); } }); var list = document.getElementById('msgList'); socket.on('message', function(msg) { var li = document.createElement('li'); li.innerHTML = msg; list.insertBefore(li, list.childNodes[0]); }); var btn = document.getElementById('send'); btn.addEventListener('click', sendFunc); }; </script> </head> <body onload="init();"> Message: <input id="message" /> <button type="button" id="send">Send</button><br/> <ul id="msgList"></ul> </body> </html>
以上程式碼就可以做到資料同步了,具體我來解釋下。
頁面很簡單,有一個 input 文字框,和一個 Send 按鈕,還有一個 ul 無序列表用來顯示使用者傳送的內容,當使用者在 input 文字框中輸入內容後,按下 enter 鍵或者點選 Send 按鈕都會想伺服器傳送文字框中填入的內容,並且伺服器會將這條訊息原封不動地推送到所有的客戶端中,在客戶端接收到訊息後,就會想 ul 無序列表中填入訊息。
這個 Demo 在 http://socket.io/get-started/chat/ 這上面比我講得清楚,大家可以到上面詳細閱讀,會理解得更全面一點。
由於篇幅的問題,我今天就介紹到這了,下一篇,我們將重點介紹前面說到的結合 HT for Web 的 3D 拓撲圖元件來展現實時資料通訊的效果,讓每個客戶端都同步操作,效果圖如上。