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

原文地址:https://www.cnblogs.com/meteoric_cry/p/2608959.html