Node.js + WebSocket 實現的簡易聊天室


本實例程序在Windows下測試通過。

上述實例支持以下瀏覽器:

Firefox 7-9 (Old) (Protocol Version 8)
Firefox 10+ (Protocol Version 13)
Chrome 14,15 (Old) (Protocol Version 8)
Chrome 16+ (Protocol Version 13)
Internet Explorer 10 (Preview) (Protocol Version 13)

 

消息的傳遞也比較簡單,Client –> Server, Server –> Client

 

服務器廣播消息

 

 

數據傳輸使用的是JSON格式,前台建立連接的代碼比較簡單,ex:

   1: $(function () {
   2:     window.WebSocket = window.WebSocket || window.MozWebSocket;
   3:  
   4:     var connection = new WebSocket('ws://127.0.0.1:1337');
   5:  
   6:     connection.onopen = function () {
   7:         //已建立連接
   8:     };
   9:  
  10:     connection.onerror = function (error) {
  11:         //接收或發送消息時遇到了錯誤
  12:     };
  13:  
  14:     connection.onmessage = function (message) {
  15:         
  16:         try {
  17:             var json = JSON.parse(message.data);
  18:         } catch (e) {
  19:             console.log('不能被正常解析的數據:', message.data);
  20:             return;
  21:         }
  22:  
  23:         // todo
  24:     };
  25: });

后端的實現,直接使用別人寫好的模塊所以傳統比較簡單一點(想在Windows下運行chat-server還是有點麻煩的),因為該模塊在Windows下安裝時,需要Microsoft Visual C++和Python 2.7的支持。--如果沒有安裝這兩個東東,還得先安裝一下。

如果順利的話,會看到如下圖所示的界面:

這樣我們就可以創建Server了,實現的代碼也並不復雜:

   1: var WebSocketServer = require('websocket').server;
   2: var http = require('http');
   3:  
   4: var server = http.createServer(function(request, response) {
   5:     console.log((new Date()) + ' Received request for ' + request.url);
   6:     response.writeHead(404);
   7:     response.end();
   8: });
   9: server.listen(8080, function() {
  10:     console.log((new Date()) + ' Server is listening on port 8080');
  11: });
  12:  
  13: wsServer = new WebSocketServer({
  14:     httpServer: server,
  15:     // You should not use autoAcceptConnections for production
  16:     // applications, as it defeats all standard cross-origin protection
  17:     // facilities built into the protocol and the browser.  You should
  18:     // *always* verify the connection's origin and decide whether or not
  19:     // to accept it.
  20:     autoAcceptConnections: false
  21: });
  22:  
  23: function originIsAllowed(origin) {
  24:   // put logic here to detect whether the specified origin is allowed.
  25:   return true;
  26: }
  27:  
  28: wsServer.on('request', function(request) {
  29:     if (!originIsAllowed(request.origin)) {
  30:       // Make sure we only accept requests from an allowed origin
  31:       request.reject();
  32:       console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
  33:       return;
  34:     }
  35:  
  36:     var connection = request.accept('echo-protocol', request.origin);
  37:     console.log((new Date()) + ' Connection accepted.');
  38:     connection.on('message', function(message) {
  39:         if (message.type === 'utf8') {
  40:             console.log('Received Message: ' + message.utf8Data);
  41:             connection.sendUTF(message.utf8Data);
  42:         }
  43:         else if (message.type === 'binary') {
  44:             console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
  45:             connection.sendBytes(message.binaryData);
  46:         }
  47:     });
  48:     connection.on('close', function(reasonCode, description) {
  49:         console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
  50:     });
  51: });

從上述的例子中可以看出,websocket支持兩種傳遞方式:二進制流、utf8的文本流。前面的例子中所使用的是utf8文本流

 

完整的chat-server.js的代碼如下:

   1: // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
   2: "use strict";
   3:  
   4: // Optional. You will see this name in eg. 'ps' or 'top' command
   5: process.title = 'node-chat';
   6:  
   7: //websocket服務器監聽的端口
   8: var webSocketsServerPort = 1337;
   9:  
  10: var webSocketServer = require('websocket').server;
  11: var http = require('http');
  12:  
  13: //保存最近100條消息記錄
  14: var history = [ ];
  15:  
  16: //當前連接的客戶端
  17: var clients = [ ];
  18:  
  19: /**
  20:  * 對聊天內容進行字符轉義
  21:  */
  22: function htmlEntities(str) {
  23:     return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  24: }
  25:  
  26:  
  27: var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
  28: colors.sort(function(a,b) { return Math.random() > 0.5; } );
  29:  
  30: /**
  31:  * HTTP server
  32:  */
  33: var server = http.createServer(function(request, response) {});
  34:  
  35: server.listen(webSocketsServerPort, function() {
  36:     console.log(getNow() + " WebSocket Server is listening on port:" + webSocketsServerPort);
  37: });
  38:  
  39: /**
  40:  * WebSocket server
  41:  * WebSocket server is tied to a HTTP server. To be honest I don't understand why.
  42:  */
  43: var wsServer = new webSocketServer({
  44:     httpServer: server
  45: });
  46:  
  47: //每一個客戶端請求建立連接時,都將觸發此方法
  48: wsServer.on('request', function(request) {
  49:  
  50:     console.log(getNow() + ' ' + request.origin + ' 請求連接.');
  51:  
  52:     // accept connection - you should check 'request.origin' to make sure that client is connecting from your website
  53:     // (http://en.wikipedia.org/wiki/Same_origin_policy)
  54:     var connection = request.accept(null, request.origin); 
  55:  
  56:     //保存當前請求連接客戶端的索引,以方便在斷開連接時,從連接池中移除該連接
  57:     var index = clients.push(connection) - 1;
  58:     var userName;
  59:     var userColor;
  60:  
  61:     console.log(getNow() + ' 已建立連接...');
  62:     
  63:     //推送歷史聊天記錄
  64:     if (history.length > 0) {
  65:         connection.sendUTF(JSON.stringify({type: 'history', data: history}));
  66:     }
  67:  
  68:     //某一客戶端發送消息過來
  69:     connection.on('message', function(message) {
  70:         if (message.type === 'utf8') {
  71:             
  72:             //第一次請求用於保存用戶信息
  73:             if (!userName) {
  74:                 userName = htmlEntities(message.utf8Data);
  75:                 
  76:                 userColor = colors.shift();
  77:                 connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
  78:                 console.log(getNow() + ' 用戶已登錄: ' + userName + ' -- ' + userColor);
  79:  
  80:             } else {
  81:                 //記錄消息並廣播
  82:                 console.log(getNow() + userName + '-說: ' + message.utf8Data);
  83:                 
  84:                 //傳遞給客戶端的數據格式
  85:                 var obj = {
  86:                     time: (new Date()).getTime(),
  87:                     text: htmlEntities(message.utf8Data),
  88:                     author: userName,
  89:                     color: userColor
  90:                 };
  91:                 history.push(obj);
  92:  
  93:                 //取數組最后100條消息記錄並保存
  94:                 history = history.slice(-100); 
  95:  
  96:                 //將消息廣播給所有客戶端
  97:                 var json = JSON.stringify({ type:'message', data: obj });
  98:                 for (var i=0; i < clients.length; i++) {
  99:                     clients[i].sendUTF(json);
 100:                 }
 101:                 
 102:                 console.log("總的客戶端連接數:" + clients.length);
 103:             }
 104:         }
 105:     });
 106:  
 107:     //用戶斷開連接
 108:     connection.on('close', function(connection) {
 109:         if (!userName && !userColor) {
 110:             console.log(getNow() + " -- " + connection.remoteAddress + " 斷開鏈接.");
 111:             
 112:             //從連接池中移除連接
 113:             clients.splice(index, 1);
 114:             
 115:             //回收訪用戶所使用的顏色
 116:             colors.push(userColor);
 117:         }
 118:     });
 119:  
 120: });
 121:  
 122: function getNow() {
 123:     return new Date().format('yyyy-MM-dd hh:mm:ss');
 124: }
 125:  
 126: Date.prototype.format = function (fmt) { //author: meizz   
 127:     var o = {
 128:         "M+": this.getMonth() + 1,
 129:         "d+": this.getDate(),   
 130:         "h+": this.getHours(),   
 131:         "m+": this.getMinutes(),  
 132:         "s+": this.getSeconds(),   
 133:         "q+": Math.floor((this.getMonth() + 3) / 3),   
 134:         "S": this.getMilliseconds()   
 135:     };
 136:     if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
 137:     for (var k in o)
 138:     if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
 139:     return fmt;
 140: }

運行supervisor chat-server.js或者node chat-server.js 就OK了~

 

使用Firefox測試一下:)

image

 

本文參考:

1、Node.js & WebSocket - Simple chat tutorial

2、WebSocket-Node


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM