socket.io实现多人聊天室(html+node.js)

4,013 阅读2分钟

前言

最近由于新冠肺炎的影响,相信大家此时都是在家办公吧,小区限制3天一人一票的门禁卡,让我们只能家里蹲,不过作为程序员的我们,一台电脑足够让我们充实的过完每一天,上班敲敲代码,累了打打游戏,烦了听听音乐,手痒了还可以打打网络麻将,岂不是美滋滋;我呢,工作快2年了,一篇文章都没发表过,最近正好工作上有一个任务,需要完成一个实时通讯的功能,不仅要能人与人聊天,还要实现机器人的自动聊天。本篇是研究的第一天,socket.io实现多人聊天室,文章内容可能比较基础,请大佬们止步于此。

socket.io

socket.ioAPI文档

socket.io封装了websocket,同时新增了很多其他的连接方式和功能,最好用的就是自定义事件,除了connect,message,disconnect这些事件的名字不能使用之外,你可以触发任何自定义的事件名称,本文只用到消息收发的功能。

消息收发

一、发送事件发送数据

socket.emit(自定义发送的字段, data);

二、监听事件获取数据

socket.on(自定义监听的字段, function(data) {
    console.log(data);
})

服务端Server

初始化项目

//初始化项目生成package.json
npm init -y

//安装依赖express+socket.io
npm install express socket.io --save

//创建文件夹
index.html+app.js

以下所有都是app.js文件下的内容:

创建socket.io实例

//引入依赖并创建实例
let app = require('express')();//引入express模块
let http = require('http').Server(app);//引入HTTP模块并启动服务
let io = require('socket.io')(http);//引入socket.io模块

//监听端口
const port = 3000;
http.listen(port,()=>{
    console.log(`Http is listening to port ${port}`);
})

开始搭建连接

io.on('connection',function(socket){
    
})

发送和接收事件

在写事件的时候我们首先要考虑几个问题:

  • 1.我们要知道哪些用户进入聊天室,即需要一个变量来保存正在聊天室的用户信息(users={});
  • 2.我们需要给用户提供在线用户列表(onlineUsers=[]),使用户可以单独给某个好友发送消息;
  • 3.总结前2个需求,需要监听前台用户发送的3个事件(1登录事件,2发送消息事件,3退出事件),需要向前台用户发送2个事件(1登录成功后提供在线用户列表,2发送消息后向某位用户私发消息);
//创建变量
let users = {};//接收所有用户的对象{name1,name2,name3}
let onlineUsers = [];//接收所有在线用户的数组[{name:,id:},{name:,id:}]or[name1,name2,name3]

//创建事件
io.on('connection',function(socket){
// 监听发私有消息事件==private message
    socket.on('private message',(from,to,msg)=>{
      if(to in users){
        users[to].emit('to'+to,{msg,from,to});
      }
    });
    // 监听新用户进入聊天室事件==new user
    socket.on('new user',name=>{
      // 只有当该用户不在用户列表中才进行操作
      if(!(name in users)){
        users[name] = socket;//将用户私有的socket保存给用户
        onlineUsers.push(name);//将用户保存到在线列表
      }
      io.emit('online users',onlineUsers);//告诉前台用户在线用户列表
    });
    // 监听用户退出连接==disconnect
    socket.on('disconnect',()=>{
      let logoutUserName;
      // 遍历所有用户,判断用户是否正在聊天室
      for(let obj in users){
        // 如果在聊天室,提示退出,并在users中移除
        if(users[obj]==socket){
          logoutUserName = obj;
          delete users[obj];
        }
      }
      // 遍历所有在线用户,判断退出的用户是否在线
      for(let i in onlineUsers){
        // 如果该用户在线的话,移除用户
        if(onlineUsers[i]==logoutUserName){
          onlineUsers.splice(i,1);
        }
      }
      // 告诉前台在线用户列表
      io.emit('online users',onlineUsers);
      // 告诉所有用户该用户已离开聊天室
      io.emit('user disconnected',logoutUserName);
    })
})

到此,服务端的工作已完成,下面是客户端的。

以下所有都是index.html文件下的内容:

客户端Client

因为只是为了测试,客户端的页面我做的很简单,只是为了测试功能,下面是源代码:


<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
    <title>多人聊天室</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script src="http://localhost:3000/socket.io/socket.io.js"></script>
    <style>
      li{
        list-style: none;
        margin-bottom: 10px;
      }
      #message_list li{
        height:40px;
        clear: both;
      }
      li span.name{
        max-width: 300px;
        padding:10px 20px;
        background:#af1;
        border-radius: 5px;
      }
      li i{
        margin:0 10px;
        display: inline-block;
        font-style: normal;
        width:40px;height:40px;
        text-align: center;
        line-height: 40px;
        border-radius: 50%;
        border:1px solid #af1;
      }
      #message_list{
        padding:20px;
        max-width: 600px;
        min-height:300px;
        border:1px solid #ccc;
      }
      .rt{
        float: right;
      }
    </style>
  </head>
  <body>
    <ul id="messages">
      <li>即将进行推送:</li>
    </ul>
    <!-- 登录接口 -->
    <div id='login_div'>
      姓名:<input id="user_name" type="text"><button type="button" id="login">登录</button>
    </div>
    <!-- 登录之后聊天界面 -->
    <div id="div"  style="display:none">
      <p>在线用户:<span id='online'></span></p>
      <p>聊天用户:<select id="select"></select><br></p>
      <p>消息内容:<input type="text" id="message"><button type="button" id="send">发送</button></p>
      <p>消息列表:</p>
      <ul id="message_list"></ul>
      <p>实时动态:</p>
      <ul id='message_status'></ul>
    </div>
    <script>
      const ip = 'localhost';
      const port = 3000;
      $(function(){
        var onlineUsers = [],socket,userName = '';
        //登录
        $("#login").on("click",function(){
          $('#login_div').hide()
          socket = io(`ws://${ip}:${port}`);
          socket.on('connect', function (data) {              
            userName = $('#user_name').val();
            socket.emit('new user',userName);
            $('#div').show();
            //接受私有聊天信息
            socket.on('to'+userName, function (data) {
              let $message_list = $('#message_list');
              $message_list.append(`<li><span><span class='name'>${data.msg}&nbsp;<small>from:${data.from}</small></span></span></li>`);
            });
            //获得当前在线人员
            socket.on("online users",function(data){
              if(data.length>onlineUsers.length){
                $('#message_status').append(`<li><b>${data.slice(-1)[0]}</b>进入聊天室&nbsp;${new Date().toLocaleTimeString()}</li>`);
              }
              $('#online').html(data.join(','));
              onlineUsers = data;
              console.log("刷新在线人数:"+onlineUsers.length+'人');
              let $select = $('#select');
              $select.empty();
              for(var j=0; j< onlineUsers.length; j++){
                if(userName!=onlineUsers[j]){
                  var option = $("<option value='"+onlineUsers[j]+"'>"+onlineUsers[j]+"</option>");
                  $select.append(option);
                }
              }
            });
            // 退出聊天室
            socket.on('user disconnected',function(name){
              $('#message_status').append(`<li><b>${name.slice(-1)[0]}</b>退出聊天室&nbsp;${new Date().toLocaleTimeString()}</li>`);
            })
          });
        });	
        //发送消息
        $("#send").click(function(e){     
            var msg  = $('#message').val(),
                to = $('#select').val();
            socket.emit('private message',userName,to,msg);
            let $message_list = $('#message_list');
            $message_list.append(`<li><span class='rt'><span class='name'>${msg}&nbsp;&nbsp;<small>to:${to}</small></span></span></li>`);
            $('#message').val('');
        });
      });
    </script>
  </body>
</html>

页面效果是这样的:

  • 1、未登录前:

  • 2、登录jeenk_zou用户后:

  • 3、新开一个页面再次登录一个用户Lilian:

  • 4、发送消息:

  • 5、回复消息:

没有做用户退出功能,是因为在用户登录的时候也没有记录用户的登录状态,所以刷新页面用户会自动退出。

总结

本篇只是通过两个简单的文件实现聊天功能,socket.io还有很多其他强大的功能,之后会继续研究更新其他用法。我是小白jeenk_zou,第一次写博客,欢迎各位多指点不足。