首先感谢作者windlany,能够写出简单易懂的文章,能将代码(注释也非常详细)共享出来供我学习。 https://juejin.im/post/5a73ddcff265da4e81237429
所用的技术
node + express + socket.io + jQuery
socket.io是实现websocket的库,websocket不同http是一个双向通信的协议,所以非常适合用在聊天室。
下面在作者文章和注释下,写下自己的研究。
index.html除了下面的问题,没什么好说的。
一开始我很奇怪,项目中没有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 = `
${users[i].name}
`;
$('#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的通信方式,余下的我就不一一分析了。