欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-24 16:59:59
1、前端界面代码   前端不是重点,够用就行,下面是前端界面,具体代码可到github下载。 2、服务器端搭建   本服务器需要提供两个功能:http服务和webso...

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实现多房间简易聊天室功能,希望对大家有所帮助