首页 > 代码库 > nodejs构建多房间简易聊天室

nodejs构建多房间简易聊天室

1、前端界面代码

  前端不是重点,够用就行,下面是前端界面,具体代码可到github下载。

2、服务器端搭建

  本服务器需要提供两个功能:http服务和websocket服务,由于node的事件驱动机制,可将两种服务搭建在同一个端口下。

  1、包描述文件:package.json,这里用到了两个依赖项,mime:确定静态文件mime类型,socket.io:搭建websocket服务,然后使用npm install  安装依赖

技术分享
{  "name": "chat_room",  "version": "1.0.0",  "description": "this is a room where you can chat with your friends",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "sfs",  "license": "ISC",  "dependencies": {    "socket.io":"2.0.3",    "mime":"1.3.6"  }}
View Code

  2、http服务器

  http服务主要是给web浏览器提供静态文件,既浏览器发来一个请求,服务器返回一个响应。

技术分享
 1 const  2     http=require(‘http‘), 3     fs=require(‘fs‘), 4     path=require(‘path‘), 5     mime=require(‘mime‘), 6     chatServer=require(‘./lib/chat_server‘); 7  8 var cache={};//缓存静态文件内容 9 //发送错误响应10 function send404(response){11     response.writeHead(404,{‘Content-Type‘:‘text/plain‘});12     response.write(‘Error 4.4:文件未找到。‘);13     response.end();14 }15 //发送文件内容16 function sendFile(response,filePath,fileContents){17     response.writeHead(18         200,19         {"content-Type":mime.lookup(path.basename(filePath))}20     );21     response.end(fileContents);22 }23 //查找文件24 function serveStatic(response,cache,absPath){25     if(cache[absPath]){26         sendFile(response,absPath,cache[absPath]);27     }else{28         fs.exists(absPath,function(exists){29             if(exists){30                 fs.readFile(absPath,function(err,data){31                     if(err){32                         send404(response);33                     }else{34                         cache[absPath]=data;35                         sendFile(response,absPath,data);36                     }37                 });38             }else{39                 send404(response);40             }41         });42     }43 }44 45 46 //入口47 var server=http.createServer(function(request,response){48     var filePath=false;49     console.log(`new request for ${request.url}`);50     if(request.url===‘/‘){51         filePath=‘public/index.html‘;52     }else{53         filePath=‘public‘+request.url;54     }55 56     var absPath=‘./‘+filePath;57     serveStatic(response,cache,absPath);58 });59 server.listen(3000,function(){60     console.log("the server is listening on prot 3000.");61 });62 chatServer.listen(server); //websocket服务也绑定到该端口上
View Code

  3、socket服务

  socket.io提供了开箱既用的虚拟通道,所以不需要任务手动转发消息到已连接的的用户,可以使用 socket.broadcast.to(room).emit(‘message‘,‘hello‘); room为某个聊天室id

技术分享
  1 const   2     socketio=require(‘socket.io‘);  3   4 var io,  5     guestNumber=1,  //用户编号  6     nickNames={},   //socket id对应的nickname  7     namesUsed={},   //所有已使用的nickname  8     allRooms={},    //聊天室--人数  9     currentRoom={}; //sockid--聊天室 10  11 module.exports.listen=function(server){ 12     io=socketio.listen(server); 13     io.serveClient(‘log level‘,1); 14     io.sockets.on(‘connection‘,function(socket){ 15         guestNumber=assignGuestName(socket,guestNumber,nickNames); 16         joinRoom(socket,‘Lobby‘); 17         handleMessageBroadcasting(socket,nickNames); 18         handleNameChangeAttempts(socket,nickNames,namesUsed); 19         handleRoomJoining(socket); 20         socket.on(‘rooms‘,function(){ 21             socket.emit(‘rooms‘,JSON.stringify(allRooms)); 22         }); 23         handleClientDisconnection(socket,nickNames,namesUsed); 24     }); 25 }; 26 //新socket连入,自动分配一个昵称 27 function assignGuestName(socket,guesetNumber,nickNames){ 28     var name=‘Guest‘+guestNumber; 29     nickNames[socket.id]=name; 30     socket.emit(‘nameResult‘,{ 31         success:true, 32         name:name 33     }); 34     namesUsed[name]=1; 35     return guestNumber+1; 36 } 37 //加入某个聊天室 38 function joinRoom(socket,room){ 39     socket.join(room); 40     var num=allRooms[room]; 41     if(num===undefined){ 42         allRooms[room]=1; 43     }else{ 44         allRooms[room]=num+1; 45     } 46     currentRoom[socket.id]=room; 47     socket.emit(‘joinResult‘,{room:room}); 48     socket.broadcast.to(room).emit(‘message‘,{ 49         text:nickNames[socket.id]+‘ has join ‘+room+‘.‘ 50     }); 51  52     var usersinRoom=io.sockets.adapter.rooms[room]; 53     if(usersinRoom.length>1){ 54         var usersInRoomSummary=‘Users currently in ‘+room+‘ : ‘; 55         for(var index in usersinRoom.sockets){ 56             if(index!=socket.id){ 57                 usersInRoomSummary+=nickNames[index]+‘,‘; 58             } 59         } 60         socket.emit(‘message‘,{text:usersInRoomSummary});  61     } 62 } 63 //修改昵称 64 function handleNameChangeAttempts(socket,nickNames,namesUsed){ 65     socket.on(‘nameAttempt‘,function(name){ 66         if(name.indexOf(‘Guest‘)==0){ 67             socket.emit(‘nameResult‘,{ 68                 success:false, 69                 message:‘Names cannot begin with "Guest".‘ 70             }); 71         }else{ 72             if(namesUsed[name]==undefined){ 73                 var previousName=nickNames[socket.id]; 74                 delete namesUsed[previousName]; 75                 namesUsed[name]=1; 76                 nickNames[socket.id]=name; 77                 socket.emit(‘nameResult‘,{ 78                     success:true, 79                     name:name 80                 }); 81                 socket.broadcast.to(currentRoom[socket.id]).emit(‘message‘,{ 82                     text:previousName+‘ is now known as ‘+name+‘.‘ 83                 }); 84             }else{ 85                 socket.emit(‘nameResult‘,{ 86                     success:false, 87                     message:‘That name is already in use.‘   88                 }); 89             } 90         } 91     });                                                                         92 } 93 //将某个用户的消息广播到同聊天室下的其他用户 94 function handleMessageBroadcasting(socket){ 95     socket.on(‘message‘,function(message){ 96         console.log(‘message:---‘+JSON.stringify(message)); 97         socket.broadcast.to(message.room).emit(‘message‘,{ 98             text:nickNames[socket.id]+ ‘: ‘+message.text 99         });100     });101 }102 //加入/创建某个聊天室103 function handleRoomJoining(socket){104     socket.on(‘join‘,function(room){105         var temp=currentRoom[socket.id];106         delete currentRoom[socket.id];107         socket.leave(temp);108         var num=--allRooms[temp];109         if(num==0)110             delete allRooms[temp];111         joinRoom(socket,room.newRoom);112     });113 }114 //socket断线处理115 function handleClientDisconnection(socket){116     socket.on(‘disconnect‘,function(){117         console.log("xxxx disconnect");118         allRooms[currentRoom[socket.id]]--;119         delete namesUsed[nickNames[socket.id]];120         delete nickNames[socket.id];121         delete currentRoom[socket.id];122     })123 }
View Code

3、客户端实现socket.io

  1、chat.js处理发送消息,变更房间,聊天命令。

技术分享
 1 var Chat=function(socket){ 2     this.socket=socket;//绑定socket 3 } 4 //发送消息 5 Chat.prototype.sendMessage=function(room,text){ 6     var message={ 7         room:room, 8         text:text 9     };10     this.socket.emit(‘message‘,message);11 };12 //变更房间13 Chat.prototype.changeRoom=function(room){14     this.socket.emit(‘join‘,{15         newRoom:room16     });17 };18 //处理聊天命令19 Chat.prototype.processCommand=function(command){20     var words=command.split(‘ ‘);21     var command=words[0].substring(1,words[0].length).toLowerCase();22     var message=false;23 24     switch(command){25         case ‘join‘:26             words.shift();27             var room=words.join(‘ ‘);28             this.changeRoom(room);29             break;30         case ‘nick‘:31             words.shift();32             var name=words.join(‘ ‘);33             this.socket.emit(‘nameAttempt‘,name);34             break;35         default:36             message=‘Unrecognized command.‘;37             break;38     }39     return message;40 }; 
View Code

  2、chat_ui.js 处理用户输入,根据输入调用chat.js的不同方法发送消息给服务器

技术分享
 1 function divEscapedContentElement(message){ 2     return $(‘<div></div>‘).text(message); 3 } 4 function divSystemContentElement(message){ 5     return $(‘<div></div>‘).html(‘<i>‘+message+‘</i>‘); 6 } 7 function processUserInput(chatApp,socket){ 8     var message=$(‘#send-message‘).val(); 9     var systemMessage;10     if(message.charAt(0)==‘/‘){11         systemMessage=chatApp.processCommand(message);12         if(systemMessage){13             $(‘#messages‘).append(divSystemContentElement(systemMessage));14         }15     }else{16         chatApp.sendMessage($(‘#room‘).text(),message);17         $(‘#messages‘).append(divSystemContentElement(message));18         $(‘#messages‘).scrollTop($(‘#messages‘).prop(‘scrollHeight‘));19     }20     $(‘#send-message‘).val(‘‘);21 }
View Code

  3、init.js客户端程序初始化   创建一个websocket连接,绑定事件。

技术分享
 1 if(window.WebSocket){ 2     console.log(‘This browser supports WebSocket‘); 3 }else{ 4     console.log(‘This browser does not supports WebSocket‘); 5 } 6 var socket=io.connect(); 7 $(document).ready(function(){ 8     var chatApp=new Chat(socket); 9     socket.on(‘nameResult‘,function(result){10         var message;11         if(result.success){12             message=‘You are known as ‘+result.name+‘.‘;13         }else{14             message=result.message;15         }16         console.log("nameResult:---"+message);17         $(‘#messages‘).append(divSystemContentElement(message));18         $(‘#nickName‘).text(result.name);19     });20 21     socket.on(‘joinResult‘,function(result){22         console.log(‘joinResult:---‘+result);23         $(‘#room‘).text(result.room);24         $(‘#messages‘).append(divSystemContentElement(‘Room changed.‘));25     });26 27     socket.on(‘message‘,function(message){28         console.log(‘message:---‘+message);29         var newElement=$(‘<div></div>‘).text(message.text);30         $(‘#messages‘).append(newElement);31         $(‘#messages‘).scrollTop($(‘#messages‘).prop(‘scrollHeight‘));32     });33 34     socket.on(‘rooms‘,function(rooms){35         console.log(‘rooms:---‘+rooms);36         rooms=JSON.parse(rooms);37         $(‘#room-list‘).empty();38         for(var room in rooms){39             $(‘#room-list‘).append(divEscapedContentElement(room+‘:‘+rooms[room]));40         }41         $(‘#room-list div‘).click(function(){42             chatApp.processCommand(‘/join ‘+$(this).text().split(‘:‘)[0]);43             $(‘#send-message‘).focus();44         });45     });46 47     setInterval(function(){48         socket.emit(‘rooms‘);49     },1000);50 51     $(‘#send-message‘).focus();52     $(‘#send-button‘).click(function(){53         processUserInput(chatApp,socket);54     });55 });
View Code

完整代码,可到https://github.com/FleyX/ChatRoom 下载。

  

nodejs构建多房间简易聊天室