天天看点

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的支持。--如果没有安装这两个东东,还得先安装一下。

如果顺利的话,会看到如下图所示的界面:

Node.js + WebSocket 实现的简易聊天室

这样我们就可以创建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了~

Node.js + WebSocket 实现的简易聊天室
Node.js + WebSocket 实现的简易聊天室

使用Firefox测试一下:)

Node.js + WebSocket 实现的简易聊天室

本文参考:

1、Node.js & WebSocket - Simple chat tutorial

2、WebSocket-Node