#0 系列目錄#javascript
WebSocket是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通信的網絡技術
。在WebSocket API中,瀏覽器和服務器只須要要作一個握手(handshaking)的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。css
WebSocket是一個通訊的協議,分爲服務器和客戶端。服務器放在後臺,保持與客戶端的長鏈接,完成雙方通訊的任務。客戶端通常都是實如今支持HTML5瀏覽器核心中,經過提供Javascript API使用網頁能夠創建websocket鏈接。html
#1 爲何用Nodejs#前端
固然Nodejs也有一些缺點:java
#2 WebSocket-Node# WebSocket-Node,是一個簡單的庫,不只支持draft-10,還有以前的各類版本。項目地址:https://github.com/Worlize/WebSocket-Node。node
服務器端配置:git
localhost:king-node TaoBangren$ npm install websocket > websocket@1.0.22 install /Users/TaoBangren/git@osc/king-node/node_modules/websocket > (node-gyp rebuild 2> builderror.log) || (exit 0) CXX(target) Release/obj.target/bufferutil/src/bufferutil.o SOLINK_MODULE(target) Release/bufferutil.node CXX(target) Release/obj.target/validation/src/validation.o SOLINK_MODULE(target) Release/validation.node king-node@0.0.0 /Users/TaoBangren/git@osc/king-node └── websocket@1.0.22 extraneous
增長app.js:github
~ vi app.js // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ "use strict"; // Optional. You will see this name in eg. 'ps' or 'top' command process.title = 'node-chat'; // Port where we'll run the websocket server var webSocketsServerPort = 3001; // websocket and http servers var webSocketServer = require('websocket').server; var http = require('http'); /** * Global variables */ // latest 100 messages var history = [ ]; // list of currently connected clients (users) var clients = [ ]; /** * Helper function for escaping input strings */ function htmlEntities(str) { return String(str).replace(/&/g, '&').replace(/>/g, '>').replace(/"/g, '"'); } // Array with some colors var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ]; // ... in random order colors.sort(function(a,b) { return Math.random() > 0.5; } ); /** * HTTP server */ var server = http.createServer(function(request, response) { // Not important for us. We're writing WebSocket server, not HTTP server }); server.listen(webSocketsServerPort, function() { console.log((new Date()) + " Server is listening on port " + webSocketsServerPort); }); /** * WebSocket server */ var wsServer = new webSocketServer({ // WebSocket server is tied to a HTTP server. WebSocket request is just // an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6 httpServer: server }); // This callback function is called every time someone // tries to connect to the WebSocket server wsServer.on('request', function(request) { console.log((new Date()) + ' Connection from origin ' + request.origin + '.'); // accept connection - you should check 'request.origin' to make sure that // client is connecting from your website // (http://en.wikipedia.org/wiki/Same_origin_policy) var connection = request.accept(null, request.origin); // we need to know client index to remove them on 'close' event var index = clients.push(connection) - 1; var userName = false; var userColor = false; console.log((new Date()) + ' Connection accepted.'); // send back chat history if (history.length > 0) { connection.sendUTF(JSON.stringify( { type: 'history', data: history} )); } // user sent some message connection.on('message', function(message) { if (message.type === 'utf8') { // accept only text if (userName === false) { // first message sent by user is their name // remember user name userName = htmlEntities(message.utf8Data); // get random color and send it back to the user userColor = colors.shift(); connection.sendUTF(JSON.stringify({ type:'color', data: userColor })); console.log((new Date()) + ' User is known as: ' + userName + ' with ' + userColor + ' color.'); } else { // log and broadcast the message console.log((new Date()) + ' Received Message from ' + userName + ': ' + message.utf8Data); // we want to keep history of all sent messages var obj = { time: (new Date()).getTime(), text: htmlEntities(message.utf8Data), author: userName, color: userColor }; history.push(obj); history = history.slice(-100); // broadcast message to all connected clients var json = JSON.stringify({ type:'message', data: obj }); for (var i=0; i < clients.length; i++) { clients[i].sendUTF(json); } } } }); // user disconnected connection.on('close', function(connection) { if (userName !== false && userColor !== false) { console.log((new Date()) + " Peer " + connection.remoteAddress + " disconnected."); // remove user from the list of connected clients clients.splice(index, 1); // push back user's color to be reused by another user colors.push(userColor); } }); });
啓動服務器:web
~ D:\workspace\javascript\nodejs-websocket>node app.js Warning: Native modules not compiled. XOR performance will be degraded. Warning: Native modules not compiled. UTF-8 validation disabled. Wed Aug 21 2013 15:28:48 GMT+0800 (中國標準時間) Server is listening on port 3001 Wed Aug 21 2013 15:28:53 GMT+0800 (中國標準時間) Connection from origin null. Wed Aug 21 2013 15:28:53 GMT+0800 (中國標準時間) Connection accepted. Wed Aug 21 2013 15:28:53 GMT+0800 (中國標準時間) User is known as: Conan with red color. Wed Aug 21 2013 15:28:53 GMT+0800 (中國標準時間) Peer undefined disconnected.
咱們看到XOR 和UTF-8 validation 兩個模塊是被禁止的,不過不影響測試。express
客戶端,仍是使用同樣的。
另外,WebSocket-Node還提供C/S模式的交互,能夠不使用瀏覽器:
服務端代碼:
~ vi app2.js #!/usr/bin/env node var WebSocketServer = require('websocket').server; var http = require('http'); var server = http.createServer(function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }); server.listen(3001, function() { console.log((new Date()) + ' Server is listening on port 3001'); }); wsServer = new WebSocketServer({ httpServer: server, // You should not use autoAcceptConnections for production // applications, as it defeats all standard cross-origin protection // facilities built into the protocol and the browser. You should // *always* verify the connection's origin and decide whether or not // to accept it. autoAcceptConnections: false }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); return; } var connection = request.accept('echo-protocol', request.origin); console.log((new Date()) + ' Connection accepted.'); connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); connection.sendUTF(message.utf8Data); } else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); } }); connection.on('close', function(reasonCode, description) { console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); }); });
客戶端代碼:
~ vi client.js #!/usr/bin/env node var WebSocketClient = require('websocket').client; var client = new WebSocketClient(); client.on('connectFailed', function(error) { console.log('Connect Error: ' + error.toString()); }); client.on('connect', function(connection) { console.log('WebSocket client connected'); connection.on('error', function(error) { console.log("Connection Error: " + error.toString()); }); connection.on('close', function() { console.log('echo-protocol Connection Closed'); }); connection.on('message', function(message) { if (message.type === 'utf8') { console.log("Received: '" + message.utf8Data + "'"); } }); function sendNumber() { if (connection.connected) { var number = Math.round(Math.random() * 0xFFFFFF); connection.sendUTF(number.toString()); setTimeout(sendNumber, 1000); } } sendNumber(); }); client.connect('ws://localhost:3001/', 'echo-protocol');
程序啓動:
~ D:\workspace\javascript\nodejs-websocket>node app2.js ~ D:\workspace\javascript\nodejs-websocket>node client.js
測試成功!!
#3 faye-websocket-node# faye-websocket-node,是擴展faye項目而開發的websocket的一個實現。項目地址:https://github.com/faye/faye-websocket-node
服務器端配置:
~ D:\workspace\javascript>mkdir nodejs-faye-websocket ~ D:\workspace\javascript>cd nodejs-faye-websocket ~ D:\workspace\javascript\nodejs-faye-websocket>npm install faye-websocket npm http GET https://registry.npmjs.org/faye-websocket npm http 304 https://registry.npmjs.org/faye-websocket npm http GET https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz npm http 200 https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz npm http GET https://registry.npmjs.org/websocket-driver npm http 200 https://registry.npmjs.org/websocket-driver npm http GET https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz npm http 200 https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz faye-websocket@0.6.1 node_modules\faye-websocket └── websocket-driver@0.2.2 ~ vi app.js var WebSocket = require('faye-websocket'), http = require('http'); var server = http.createServer(); server.on('upgrade', function(request, socket, body) { if (WebSocket.isWebSocket(request)) { var ws = new WebSocket(request, socket, body); ws.on('message', function(event) { ws.send(event.data); }); ws.on('close', function(event) { console.log('close', event.code, event.reason); ws = null; }); } }); server.listen(3001); ~ D:\workspace\javascript\nodejs-faye-websocket>node app.js
用網頁客戶端訪問:
測試成功,很是簡單!!並且,沒有依賴其餘的庫!!
#4 socket.io# 項目地址:https://github.com/LearnBoost/socket.io
環境配置:
~ D:\workspace\javascript>express -e nodejs-socketio ~ D:\workspace\javascript>cd nodejs-socketio && npm install ~ D:\workspace\javascript\nodejs-socketio>npm install socket.io
修改app.js配置文件
~ vi app.js var app = require('express')(); var server = require('http').createServer(app); var io = require('socket.io').listen(server); server.listen(80); app.get('/', function (req, res) { res.sendfile(__dirname + '/client/index.html'); }); io.sockets.on('connection', function (socket) { socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); });
增長客戶端文件,注意這個的index.html要根據app.js指定的位置」res.sendfile(__dirname + ‘/client/index.html’);」
~ mkdir client ~ vi /client/index.html <!DOCTYPE html> <html> <head> <title>socket.io</title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>socket.io</h1> <p>Welcome to socket.io</p> <script src="/socket.io/socket.io.js"></script> <script> var socket = io.connect('http://localhost'); socket.on('news', function (data) { console.log(data); socket.emit('my other event', { my: 'data' }); }); </script> </body> </html>
啓動服務器:
~ D:\workspace\javascript\nodejs-socketio>node app.js info - socket.io started
打開瀏覽器: http://localhost
查看服務器日誌:
debug - served static content /socket.io.js debug - client authorized info - handshake authorized ZR-xQhsKCCqM03TRHW4b debug - setting request GET /socket.io/1/websocket/ZR-xQhsKCCqM03TRHW4b debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b debug - client authorized for debug - websocket writing 1:: debug - websocket writing 5:::{"name":"news","args":[{"hello":"world"}]} { my: 'data' } debug - emitting heartbeat for client ZR-xQhsKCCqM03TRHW4b debug - websocket writing 2:: debug - set heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b debug - got heartbeat packet debug - cleared heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b
測試成功。
#5 總結#
node-websocket:須要依賴於底層的C++,Python的環境,支持以node作客戶端的訪問。
faye-websocket-node:是faye軟件框架體系的一部分,安裝簡單,不須要其餘依賴庫。
socket.io:功能強大,支持集成websocket服務器端和Express3框架與一身。