首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs构建多房间简易聊天室

nodejs构建多房间简易聊天室

作者头像
用户2038589
发布2018-09-06 15:34:50
2K0
发布2018-09-06 15:34:50
举报
文章被收录于专栏:青青天空树青青天空树

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"
  }
}

  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服务也绑定到该端口上

  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 }

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:room
16     });
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 }; 

  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 }

  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 });

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-06-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档