每日JavaScript之socket.io实现聊天室

首先感谢作者windlany,能够写出简单易懂的文章,能将代码(注释也非常详细)共享出来供我学习。 https://juejin.im/post/5a73ddcff265da4e81237429
所用的技术

node + express +  socket.io + jQuery

socket.io是实现websocket的库,websocket不同http是一个双向通信的协议,所以非常适合用在聊天室。
下面在作者文章和注释下,写下自己的研究。
index.html除了下面的问题,没什么好说的。

<script src="/socket.io/socket.io.js"></script>

一开始我很奇怪,项目中没有socket.io文件夹也没有socket.io.js这是从哪引入这个JS文件的?后来阅读
https://blog.csdn.net/wang839305939/article/details/79316152?utm_source=blogxgwz9
socket(server)其实在启动websocket服务的时候,同时还相当于给这个http服务增加了一条处理/socket.io/socket.io.js的路由。
对应的代码是

const http = require('http').Server(app);
// io-server
const io = require("socket.io")(http);

我们打开server.js 研究吧。这个文件的作用是用express搭建一个静态资源服务器,socket.io为这个服务器启动websocket。而另一个文件chat-client.js就是客户端。他们就像两个热恋的恋人(可能不知两个,其中一个非常花心有好几个对象),不停的诉说或倾听对方的话。下面就看看他们的对话吧 。

注意:

/* 
   io.emit(foo); //会触发所有用户的foo事件
   socket.emit(foo); //只触发当前用户的foo事件
   socket.broadcast.emit(foo); //触发除了当前用户的其他用户的foo事件
*/
const express = require('express');
const app = express();
const http = require('http').Server(app);
// 思考:socket.io作为一个函数,当前http作为参数传入生成一个io对象?
// io-server
const io = require("socket.io")(http);

const users = []; // 储存登录用户
const usersInfo = [];  // 存储用户姓名和头像

// 路由为/默认www静态文件夹
app.use('/', express.static(__dirname + '/www'));
 
// 每个连接的用户都有专有的socket
io.on('connection', (socket)=> {
    // 渲染在线人员
    io.emit('disUser', usersInfo);

    // 登录,检测用户名
    socket.on('login', (user)=> {
        if (users.includes(user.name)) { 
            socket.emit('loginError');
        } else {
            users.push(user.name);
            usersInfo.push(user);
            
            // 登录成功
            socket.emit('loginSuc');
            socket.nickname = user.name;
            io.emit('system', {
                name: user.name,
                status: '进入'
            });
            io.emit('disUser', usersInfo);
            console.log(users.length + ' user connect.');
        }
    });

    // 发送窗口抖动
    socket.on('shake', ()=> {
        socket.emit('shake', {
            name: '您'
        });
        socket.broadcast.emit('shake', {
            name: socket.nickname
        });
    });

    // 发送消息事件
    socket.on('sendMsg', (data)=> {
        var img = '';
        for(var i = 0; i < usersInfo.length; i++) {
            if(usersInfo[i].name == socket.nickname) {
                img = usersInfo[i].img;
            }
        }
        socket.broadcast.emit('receiveMsg', {
            name: socket.nickname,
            img: img,
            msg: data.msg,
            color: data.color,
            type: data.type,
            side: 'left'
        });
        socket.emit('receiveMsg', {
            name: socket.nickname,
            img: img,
            msg: data.msg,
            color: data.color,
            type: data.type,
            side: 'right'
        });
    });  

    // 断开连接时
    socket.on('disconnect', ()=> {
        var index = users.indexOf(socket.nickname); 
        if (index > -1 ) {  // 避免是undefined
            users.splice(index, 1);  // 删除用户信息
            usersInfo.splice(index, 1);  // 删除用户信息

            io.emit('system', {  // 系统通知
                name: socket.nickname,
                status: '离开'
            });
            
            io.emit('disUser', usersInfo);  // 重新渲染
            console.log('a user left.');
        }
    });
});

http.listen(3000, function() {
    console.log('listen 3000 port.');
});
io.on('connection', (socket)=> {

首先,客户端都有一个专用的socket。上面的语句意思是服务器一直监听着与客户端的连接事件connection。

io.emit('disUser', usersInfo);

然后io就会在所有socket触发disUser(展示用户)事件,并向socket传递usersInfo。
很容易想到,客户端这时应该监听disUser这个事件,并把usersInfo的信息渲染到页面。
这时我们,打开客户端的文件chat-client.js。(作者在这里用了jQuery操作DOM)

$(function() {

这行代码意识是页面加载时,执行该函数在window.onLoad事件前。

// io-client
    // 连接成功会触发服务器端的connection事件
    const socket = io(); 

这里,可以看到为每个客户端创建了一个socket。那么来找一下,这个socket是否监听了disUser事件?
果然不出所料,找到了。

// 显示在线人员
    socket.on('disUser', (usersInfo)=> {
      displayUser(usersInfo);
    });

在客户端调用了displayUser函数把usersInfo渲染出来

function displayUser(users) {
      $('#users').text(''); // 每次都要重新渲染
      if(!users.length) {
        $('.contacts p').show();
      } else {
        $('.contacts p').hide();
      }
      $('#num').text(users.length);
      for(var i = 0; i < users.length; i++) {
        var $html = `<li>
          <img src="${users[i].img}">
          <span>${users[i].name}</span>
        </li>`;
        $('#users').append($html);
      }
    }

看回到server.js

socket.on('login', (user)=> {
        if (users.includes(user.name)) { 
            socket.emit('loginError');
        } else {
            users.push(user.name);
            usersInfo.push(user);
            
            // 登录成功
            socket.emit('loginSuc');
            socket.nickname = user.name;
            io.emit('system', {
                name: user.name,
                status: '进入'
            });
            io.emit('disUser', usersInfo);
            console.log(users.length + ' user connect.');
        }
    });

我们看到socket监听了login事件,是为了向其他用户发送进入的信息,再到客户端看看怎么触发login事件

function inputName() {
      var imgN = Math.floor(Math.random()*4)+1; // 随机分配头像
      if($('#name').val().trim()!=='')
          socket.emit('login', { 
            name: $('#name').val(),
            img: 'image/user' + imgN + '.jpg'
          });  // 触发登录事件
      return false; 
    }
$('#nameBtn').click(inputName);
    // 登录成功,隐藏登录层
    socket.on('loginSuc', ()=> { 
      $('.name').hide(); 
    })

这就是客户端和服务端通过websocket的通信方式,余下的我就不一一分析了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值