socket.io + node 简单聊天室
首先,我想感谢提供该文章主要思想的作者。其中大半代码来源于该作者。个人只是在理解的基础下稍作完善。此文章的作用主要是学习与分享。
// 作者:luck_lin
// 来源:CSDN
// 原文:https://blog.csdn.net/qwe502763576/article/details/79672766
- 优化的点之一:登陆时只会通知在线的用户。(如果同时开多个客户端)
- 优化点之二:退出时,退出的用户自动回显到登陆前页面。在线的用户则收到用户退出的通知。
- 优化点三:登陆与退出,各客户端互不影响。
代码
node的安装与包的拉取我就不说了,请自觉学习。
服务端 app.js
var express = require("express");
var app = express();
//因为socket.io依赖原生http服务,所以要引入原生http模块,并且调用它的Server()方法将app传入,这样原生的http就拥有了express的方法
var http = require("http").Server(app);
var socket = require("socket.io")(http); //传入http执行socket函数,这样socket就跟http具有同一个端口了
http.listen(9999);
app.use(express.static("./html"));
//设置静态资源路径
// 创建socket监听服务:
socket.on("connection", function(ws) {
ws.on("diyLogin", function(val) { //定义一个完全自定义事件,用来获取前端发来的登录的用户名
var address = ws.handshake.address;
console.log(address);
var u = checkUser(val);
if (u.err) {
ws.emit("exist", u);
} else {
socket.sockets.emit("allLogin", u); //执行前端事件,把登陆的信息,发送范围为每一个在线的客户端
}
}); // 监测客户端的sendMsg个人用户消息发送
ws.on("sendMsg", function(msgObj) {
socket.sockets.emit("accept", msgObj);//把接收到的某个用户消息发送到所有客户端
})
// 监测客户端的exit退出
ws.on('exit', function(data) {
exit(data.user);
socket.sockets.emit("already exits", data.user);// 将退出的用户信息告知所有的客户端
})
// ws.on('close exist', function(user) { // if (user !== '') { // exit(user); // socket.sockets.emit("already exits", data.user); // 将退出的用户信息告知所有的客户端 // } // })
ws.on('disconnect', function(data) {
console.log(data);
console.log('断开连接了'
);
// socket.sockets.emit("already exits", data.user); // 将退出的用户信息告知所有的客户端 }); // 释放资源
ws.manager.transports[socket.id].socket.setTimeout(15000);
})
var arr = [];// 将登陆用户存储到arr
function checkUser(u) { //检测用户名是否已存在
if (arr.length === 0) {
arr.push(u);
return u;
};
console.log(u);
for (var i = 0; i < arr.length; i++) {
if (arr[i] === u) {
return {
err: "用户名已存在",
users: arr
}
}
}
arr.push(u);
return u;
}
// 退出登陆时 从arr删除指定的用户
function exit(u) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === u) {
arr.splice(i, 1);
}
}}
// --------------------- // 作者:luck_lin // 来源:CSDN // 原文:https://blog.csdn.net/qwe502763576/article/details/79672766 // 版权声明:本文为博主原创文章,转载请附上博文链接!
服务端 index.html
{在app.js同级新建一个html文件夹,在里面新建index.html}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
i {
font-style: normal;
}
.chatList {
height: 200px;
border: 1px dashed green;
overflow-y: auto;
}
.myself {
text-align: right;
color: red;
}
i.lin {
font-weight: bold;
color: #ffcc01;
font-size: 16px;
}
i.other {
color: green;
}
p.me {
text-align: right;
color: orange;
}
</style>
</head>
<body>
<div class="container">
<div class="loginBox">
<input type="text" class="form-control" id="username">
<button class="btn" id="sure">登录</button>
</div>
<div class="chatBox hidden">
<div class="chatList"></div>
<textarea class="form-control" id="msg" cols="30" rows="5"></textarea>
<button class="btn" id="send">发送</button><button class="btn" id="exit" value="">退出</button>
<div class="alert alert-success hidden" role="alert"></div>
</div>
</div>
</body>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script> //链接socket
var ws = io.connect("ws://localhost:9999");
var user = '';
// ws.emit('close exist',user);
$("#sure").click(function() {
var name = $("#username").val();
if (!name) {
alert("好歹起个名呀,eg:李易峰");
} else {
//通知后台有人登陆了,socket.io都是基于事件的,要做的操作都是触发自定义事件的方式
ws.emit("diyLogin", name); //执行自定义事件传入用户名
user = name; //把当前用户名称保存到全局变量
}
}) //获取后端返回的用户是否存在的查询结果
ws.on("exist", function(re) {
if (re.err) {
alert(re.err);
console.log("已经登陆的用户:\n", re.users)
return;
}
})
//接收后端返回的全局的登录者的名字的事件
ws.on("allLogin", function(name) {
// user 为空说明当前客户端还没用户登陆,只有有人登陆的客户端才被通知
if (user !== '') {
var _alert = name === "刘欢" ? "欢迎 男爵 <i class='lin'>" + name + "</i> 骑着火麒麟来到了本直播间!" : "<i class='other'>" + name + " </i>进入直播间";
$(".alert").html(_alert).removeClass("hidden").show();
$(".loginBox").hide(); //登陆后隐藏登录盒子
$(".chatBox").removeClass("hidden"); //显示出聊天界面
setTimeout(function() {
$(".alert").fadeOut(function() {
$(this).addClass("hidden"); }
);
}, 1500)
$('#exit').val(user);
}
})
//发送聊天消息
$("#send").click(function() {
var msg = $("#msg").val();
if (!msg) {
alert("空消息也要钱哦!");
} else {
ws.emit("sendMsg", {
msg,
user
}); //执行事件把内容和发送者发送给后台,让后台返回给所有客户端
$("#msg").val("");
}
})
//接收聊天消息
ws.on("accept", function(o) {
var p = $(`<p>${o.user}: ${o.msg}</p>`)
if (o.user === user) {
p.addClass("me");
}
$(".chatList").append(p).scrollTop($(".chatList")[0].scrollHeight);
})
$('#exit').click(() => {
ws.emit("exit", {'user': user,});
});
ws.on("already exits", function(data) {
// 退出,只有对应点了退出的用户才去执行退出操作
if (data === user) {
$(".loginBox").show(); //登陆后隐藏登录盒子
$(".chatBox").addClass("hidden"); //显示出聊天界面
user = '';
} else {
//没有退出的用户则被通知有用户退出
$(".alert").html(`<i class='other'>${data} </i>退出了直播间`).removeClass("hidden").show();
setTimeout(function() {
$(".alert").fadeOut(function() {
$(this).addClass("hidden");
});
}, 1500)
}
})</script>
</html>
<!-- --------------------- 作者:luck_lin 来源:CSDN 原文:https://blog.csdn.net/qwe502763576/article/details/79672766 版权声明:本文为博主原创文章,转载请附上博文链接! -->
运行
- 安装好node,利用cmd、git、或编辑器比如vscode,其中一个,在app.js所在文件夹下 运行 node app.js
运行效果
- 进入聊天室效果。
- 退出聊天室效果
不足:
在本例子中有几个不足之处;
- 通过用户名存储的不同客户端的信息,虽然在本例子里便于操作,但是在关闭浏览器页面时,无法通过ID删除服务端里存储的客户信息,导致不重启服务的情况下(清理服务端的在线用户信息),误判断为已经在线,无法重新登录。
- 后期本人会改进,采用存储socketId的形式去改写该聊天室的例子,实现退出登陆以及关闭页面实现退出房间的操作。
本例子的知识点
- 利用node建立服务端 。
- socket.io 双向通信的使用(感兴趣的可去了解:https://www.w3cschool.cn/socket/socket-odxe2egl.html )。
- socket.io 中 emit()发送方法、on()监听方法的使用。