前言
最近由于新冠肺炎的影响,相信大家此时都是在家办公吧,小区限制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} <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>进入聊天室 ${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>退出聊天室 ${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} <small>to:${to}</small></span></span></li>`);
$('#message').val('');
});
});
</script>
</body>
</html>
页面效果是这样的:
-
1、未登录前:
-
2、登录jeenk_zou用户后:
-
3、新开一个页面再次登录一个用户Lilian:
-
4、发送消息:
-
5、回复消息:
没有做用户退出功能,是因为在用户登录的时候也没有记录用户的登录状态,所以刷新页面用户会自动退出。
总结
本篇只是通过两个简单的文件实现聊天功能,socket.io还有很多其他强大的功能,之后会继续研究更新其他用法。我是小白jeenk_zou,第一次写博客,欢迎各位多指点不足。