Nodejs实现多房间简易聊天室功能


Posted in NodeJs onJune 20, 2017

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浏览器提供静态文件,既浏览器发来一个请求,服务器返回一个响应。

const 
  http=require('http'),
  fs=require('fs'),
  path=require('path'),
  mime=require('mime'),
  chatServer=require('./lib/chat_server');

var cache={};//缓存静态文件内容
//发送错误响应
function send404(response){
  response.writeHead(404,{'Content-Type':'text/plain'});
  response.write('Error 4.4:文件未找到。');
  response.end();
}
//发送文件内容
function sendFile(response,filePath,fileContents){
  response.writeHead(
    200,
    {"content-Type":mime.lookup(path.basename(filePath))}
  );
  response.end(fileContents);
}
//查找文件
function serveStatic(response,cache,absPath){
  if(cache[absPath]){
    sendFile(response,absPath,cache[absPath]);
  }else{
    fs.exists(absPath,function(exists){
      if(exists){
        fs.readFile(absPath,function(err,data){
          if(err){
            send404(response);
          }else{
            cache[absPath]=data;
            sendFile(response,absPath,data);
          }
        });
      }else{
        send404(response);
      }
    });
  }
}
//入口
var server=http.createServer(function(request,response){
  var filePath=false;
  console.log(`new request for ${request.url}`);
  if(request.url==='/'){
    filePath='public/index.html';
  }else{
    filePath='public'+request.url;
  }

  var absPath='./'+filePath;
  serveStatic(response,cache,absPath);
});
server.listen(3000,function(){
  console.log("the server is listening on prot 3000.");
});
chatServer.listen(server); //websocket服务也绑定到该端口上

3、socket服务

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

const 
  socketio=require('socket.io');
var io,
  guestNumber=1, //用户编号
  nickNames={},  //socket id对应的nickname
  namesUsed={},  //所有已使用的nickname
  allRooms={},  //聊天室--人数
  currentRoom={}; //sockid--聊天室
module.exports.listen=function(server){
  io=socketio.listen(server);
  io.serveClient('log level',1);
  io.sockets.on('connection',function(socket){
    guestNumber=assignGuestName(socket,guestNumber,nickNames);
    joinRoom(socket,'Lobby');
    handleMessageBroadcasting(socket,nickNames);
    handleNameChangeAttempts(socket,nickNames,namesUsed);
    handleRoomJoining(socket);
    socket.on('rooms',function(){
      socket.emit('rooms',JSON.stringify(allRooms));
    });
    handleClientDisconnection(socket,nickNames,namesUsed);
  });
};
//新socket连入,自动分配一个昵称
function assignGuestName(socket,guesetNumber,nickNames){
  var name='Guest'+guestNumber;
  nickNames[socket.id]=name;
  socket.emit('nameResult',{
    success:true,
    name:name
  });
  namesUsed[name]=1;
  return guestNumber+1;
}
//加入某个聊天室
function joinRoom(socket,room){
  socket.join(room);
  var num=allRooms[room];
  if(num===undefined){
    allRooms[room]=1;
  }else{
    allRooms[room]=num+1;
  }
  currentRoom[socket.id]=room;
  socket.emit('joinResult',{room:room});
  socket.broadcast.to(room).emit('message',{
    text:nickNames[socket.id]+' has join '+room+'.'
  });
  var usersinRoom=io.sockets.adapter.rooms[room];
  if(usersinRoom.length>1){
    var usersInRoomSummary='Users currently in '+room+' : ';
    for(var index in usersinRoom.sockets){
      if(index!=socket.id){
        usersInRoomSummary+=nickNames[index]+',';
      }
    }
    socket.emit('message',{text:usersInRoomSummary}); 
  }
}
//修改昵称
function handleNameChangeAttempts(socket,nickNames,namesUsed){
  socket.on('nameAttempt',function(name){
    if(name.indexOf('Guest')==0){
      socket.emit('nameResult',{
        success:false,
        message:'Names cannot begin with "Guest".'
      });
    }else{
      if(namesUsed[name]==undefined){
        var previousName=nickNames[socket.id];
        delete namesUsed[previousName];
        namesUsed[name]=1;
        nickNames[socket.id]=name;
        socket.emit('nameResult',{
          success:true,
          name:name
        });
        socket.broadcast.to(currentRoom[socket.id]).emit('message',{
          text:previousName+' is now known as '+name+'.'
        });
      }else{
        socket.emit('nameResult',{
          success:false,
          message:'That name is already in use.' 
        });
      }
    }
  });                                    
}
//将某个用户的消息广播到同聊天室下的其他用户
function handleMessageBroadcasting(socket){
  socket.on('message',function(message){
    console.log('message:---'+JSON.stringify(message));
    socket.broadcast.to(message.room).emit('message',{
      text:nickNames[socket.id]+ ': '+message.text
    });
  });
}
//加入/创建某个聊天室
function handleRoomJoining(socket){
  socket.on('join',function(room){
    var temp=currentRoom[socket.id];
    delete currentRoom[socket.id];
    socket.leave(temp);
    var num=--allRooms[temp];
    if(num==0)
      delete allRooms[temp];
    joinRoom(socket,room.newRoom);
  });
}
//socket断线处理
function handleClientDisconnection(socket){
  socket.on('disconnect',function(){
    console.log("xxxx disconnect");
    allRooms[currentRoom[socket.id]]--;
    delete namesUsed[nickNames[socket.id]];
    delete nickNames[socket.id];
    delete currentRoom[socket.id];
  })
}

3、客户端实现socket.io

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

var Chat=function(socket){
  this.socket=socket;//绑定socket
}
//发送消息
Chat.prototype.sendMessage=function(room,text){
  var message={
    room:room,
    text:text
  };
  this.socket.emit('message',message);
};
//变更房间
Chat.prototype.changeRoom=function(room){
  this.socket.emit('join',{
    newRoom:room
  });
};
//处理聊天命令
Chat.prototype.processCommand=function(command){
  var words=command.split(' ');
  var command=words[0].substring(1,words[0].length).toLowerCase();
  var message=false;
  switch(command){
    case 'join':
      words.shift();
      var room=words.join(' ');
      this.changeRoom(room);
      break;
    case 'nick':
      words.shift();
      var name=words.join(' ');
      this.socket.emit('nameAttempt',name);
      break;
    default:
      message='Unrecognized command.';
      break;
  }
  return message;
};

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

function divEscapedContentElement(message){
  return $('<div></div>').text(message);
}
function divSystemContentElement(message){
  return $('<div></div>').html('<i>'+message+'</i>');
}
function processUserInput(chatApp,socket){
  var message=$('#send-message').val();
  var systemMessage;
  if(message.charAt(0)=='/'){
    systemMessage=chatApp.processCommand(message);
    if(systemMessage){
      $('#messages').append(divSystemContentElement(systemMessage));
    }
  }else{
    chatApp.sendMessage($('#room').text(),message);
    $('#messages').append(divSystemContentElement(message));
    $('#messages').scrollTop($('#messages').prop('scrollHeight'));
  }
  $('#send-message').val('');
}

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

if(window.WebSocket){
  console.log('This browser supports WebSocket');
}else{
  console.log('This browser does not supports WebSocket');
}
var socket=io.connect();
$(document).ready(function(){
  var chatApp=new Chat(socket);
  socket.on('nameResult',function(result){
    var message;
    if(result.success){
      message='You are known as '+result.name+'.';
    }else{
      message=result.message;
    }
    console.log("nameResult:---"+message);
    $('#messages').append(divSystemContentElement(message));
    $('#nickName').text(result.name);
  });
  socket.on('joinResult',function(result){
    console.log('joinResult:---'+result);
    $('#room').text(result.room);
    $('#messages').append(divSystemContentElement('Room changed.'));
  });
  socket.on('message',function(message){
    console.log('message:---'+message);
    var newElement=$('<div></div>').text(message.text);
    $('#messages').append(newElement);
    $('#messages').scrollTop($('#messages').prop('scrollHeight'));
  });
  socket.on('rooms',function(rooms){
    console.log('rooms:---'+rooms);
    rooms=JSON.parse(rooms);
    $('#room-list').empty();
    for(var room in rooms){
      $('#room-list').append(divEscapedContentElement(room+':'+rooms[room]));
    }
    $('#room-list div').click(function(){
      chatApp.processCommand('/join '+$(this).text().split(':')[0]);
      $('#send-message').focus();
    });
  });
  setInterval(function(){
    socket.emit('rooms');
  },1000);
  $('#send-message').focus();
  $('#send-button').click(function(){
    processUserInput(chatApp,socket);
  });
});

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

以上所述是小编给大家介绍的Nodejs实现多房间简易聊天室功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

NodeJs 相关文章推荐
nodejs 提示‘xxx’ 不是内部或外部命令解决方法
Nov 20 NodeJs
nodejs创建web服务器之hello world程序
Aug 20 NodeJs
Nodejs实战心得之eventproxy模块控制并发
Oct 27 NodeJs
Express与NodeJs创建服务器的两种方法
Feb 06 NodeJs
nodejs获取微信小程序带参数二维码实现代码
Apr 12 NodeJs
nodejs超出最大的调用栈错误问题
Dec 27 NodeJs
nodejs连接mysql数据库及基本知识点详解
Mar 20 NodeJs
nodejs搭建本地服务器轻松解决跨域问题
Mar 21 NodeJs
nodejs更改项目端口号的方法
May 13 NodeJs
对mac下nodejs 更新到最新版本的最新方法(推荐)
May 17 NodeJs
NodeJs 文件系统操作模块fs使用方法详解
Nov 26 NodeJs
nodejs微信开发之接入指南
Mar 17 NodeJs
NodeJS 实现手机短信验证模块阿里大于功能
Jun 19 #NodeJs
手把手教你把nodejs部署到linux上跑出hello world
Jun 19 #NodeJs
CentOS 安装NodeJS V8.0.0的方法
Jun 15 #NodeJs
详解Nodejs之npm&amp;package.json
Jun 15 #NodeJs
详解nodejs模板引擎制作
Jun 14 #NodeJs
Nodejs回调加超时限制两种实现方法
Jun 09 #NodeJs
nodeJS实现路由功能实例代码
Jun 08 #NodeJs
You might like
php获取mysql数据库中的所有表名的代码
2011/04/23 PHP
基于PHP Web开发MVC框架的Smarty使用说明
2013/04/19 PHP
php通过淘宝API查询IP地址归属等信息
2015/12/25 PHP
PHP实现与java 通信的插件使用教程
2019/08/11 PHP
Javascript - HTML的request类
2007/01/09 Javascript
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
javascript 设为首页与加入收藏兼容多浏览器代码
2011/01/11 Javascript
编程语言JavaScript简介
2014/10/16 Javascript
jQuery中size()方法用法实例
2014/12/27 Javascript
JavaScript数据类型检测代码分享
2015/01/26 Javascript
jQuery中extend函数详解
2015/02/13 Javascript
理解javascript中的原型和原型链
2015/07/30 Javascript
Js实现简单的小球运动特效
2016/02/18 Javascript
JS实现的颜色实时渐变效果完整实例
2016/03/25 Javascript
BootStrap中Datetimepicker和uploadify插件应用实例小结
2016/05/26 Javascript
jQuery模仿单选按钮选中效果
2016/06/24 Javascript
jQuery实现标签页效果实战(4)
2017/02/08 Javascript
AngularJS学习第二篇 AngularJS依赖注入
2017/02/13 Javascript
Node.js与Sails redis组件的使用教程
2017/02/14 Javascript
详解JS中的立即执行函数
2017/02/24 Javascript
ES6新特性之函数的扩展实例详解
2017/04/01 Javascript
ES6中的Promise代码详解
2017/10/09 Javascript
vue中子组件的methods中获取到props中的值方法
2018/08/27 Javascript
vue 实现用户登录方式的切换功能
2020/04/14 Javascript
bootstrap-table后端分页功能完整实例
2020/06/01 Javascript
python多线程下信号处理程序示例
2019/05/31 Python
浅谈Python2之汉字编码为unicode的问题(即类似\xc3\xa4)
2019/08/12 Python
Python 实现毫秒级淘宝抢购脚本的示例代码
2019/09/16 Python
从numpy数组中取出满足条件的元素示例
2019/11/26 Python
使用Bazel编译TensorBoard教程
2020/02/15 Python
实习护士自我鉴定
2013/10/13 职场文书
25岁生日感言
2014/01/13 职场文书
活动志愿者自荐信
2014/01/27 职场文书
企业后勤岗位职责
2014/02/28 职场文书
上课说话检讨书500字
2014/11/01 职场文书
2016年寒假社会实践活动总结
2015/10/10 职场文书