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应用主文件index.js
Aug 28 NodeJs
nodejs进阶(6)—连接MySQL数据库示例
Jan 07 NodeJs
NodeJS遍历文件生产文件列表功能示例
Jan 22 NodeJs
nodejs爬虫遇到的乱码问题汇总
Apr 07 NodeJs
nodejs服务搭建教程 nodejs访问本地站点文件
Apr 07 NodeJs
NodeJs form-data格式传输文件的方法
Dec 13 NodeJs
nodejs基于WS模块实现WebSocket聊天功能的方法
Jan 12 NodeJs
nodejs中express入门和基础知识点学习
Sep 13 NodeJs
NodeJS 将文件夹按照存放路径变成一个对应的JSON的方法
Oct 17 NodeJs
nodejs使用async模块同步执行的方法
Mar 02 NodeJs
nodejs和react实现即时通讯简易聊天室功能
Aug 21 NodeJs
nodejs简单抓包工具使用详解
Aug 23 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作的文本留言本的例子(四)
2006/10/09 PHP
简单采集了yahoo的一些数据
2007/02/14 PHP
几个实用的PHP内置函数使用指南
2014/11/27 PHP
PHP中抽象类、接口的区别与选择分析
2016/03/29 PHP
PHP与SQL语句常用大全
2016/12/10 PHP
PHP编辑器PhpStrom运行缓慢问题
2017/02/21 PHP
在Mac OS下搭建LNMP开发环境的步骤详解
2017/03/10 PHP
jQuery Ajax 全解析
2009/02/08 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
原生js写的放大镜效果
2012/08/22 Javascript
一个简单的网站访问JS计数器 刷新1次加1次访问
2012/09/20 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
2013/08/09 Javascript
浅谈JavaScript字符集
2014/05/22 Javascript
JavaScript生成随机字符串的方法
2015/03/19 Javascript
深入分析jsonp协议原理
2015/09/26 Javascript
JS简单实现tab切换效果的多窗口显示功能
2016/09/07 Javascript
JavaScript函数基础详解
2017/02/03 Javascript
Angularjs 实现动态添加控件功能
2017/05/25 Javascript
如何让微信小程序页面之间的通信不再变困难
2019/06/03 Javascript
JS 自执行函数原理及用法
2019/08/05 Javascript
vue 实现路由跳转时更改页面title
2019/11/05 Javascript
vue.js页面加载执行created,mounted的先后顺序说明
2020/11/07 Javascript
pytorch 使用单个GPU与多个GPU进行训练与测试的方法
2019/08/19 Python
python使用socket 先读取长度,在读取报文内容示例
2019/09/26 Python
python实现XML解析的方法解析
2019/11/16 Python
Python通过VGG16模型实现图像风格转换操作详解
2020/01/16 Python
灵活运用CSS3特性绘制简易版围棋效果
2016/09/28 HTML / CSS
意大利奢华内衣制造商:Cosabella
2017/08/29 全球购物
简述进程的启动、终止的方式以及如何进行进程的查看
2014/02/20 面试题
软件测试企业面试试卷
2016/07/13 面试题
乡镇食品安全责任书
2014/07/28 职场文书
机关作风建设工作总结
2014/10/23 职场文书
详解Redis复制原理
2021/06/04 Redis
如何使用Tkinter进行窗口的管理与设置
2021/06/30 Python
我国拿下天问一号火星着陆区附近 22 个地理实体命名:平乐、西柏坡、古田、漠河等
2022/04/29 数码科技
Spring Boot 的创建和运行示例代码详解
2022/07/23 Java/Android