socketIO
socketIO 概念
一个库,基于 Node.js 的实时应用程序框架。可以在浏览器和服务器之间实现实时,双向和基于事件的通信。它适用于每个平台、浏览器或设备,同样注重可靠性和速度。
与 websocket 关系
在websocket
出现之前,客户端和服务器之间的即时通信往往依赖于客户端进行轮询操作,websocket
使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,同时也降低了服务器的性能消耗。但是,websocket
并不能兼容所有的浏览器,所以socketIO
是不仅包含了websocket
,还对轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口
聊天练习结构
基于 socketIO 的双向通信,准备制作一个聊天界面。
前端:聊天界面的大体样式参考于微信界面后台:使用node.js
+ socketIO
在动手之前,首先要规划一下需要有的功能(虽然很多是我自己后面想到再加的 🐶 )
项目步骤
首先使用yarn init
创建一个项目
下载需要的 express 和 socket.io, 命令:yarn add socket.io express
main.js 中定义好需要 io, 并且为了防止 socketIO 连接时产生跨域问题,可以使用 cors 进行设置
const express = require('express');
const app = express();
const { Server } = require('socket.io');
const http = require('http');
const server = http.createServer(app);
const io = new Server(server, {
cors: {
allowedHeaders: ["chat-room"], // 被允许的请求头
},
});
复制代码
在 main.js 中编写 io 的连接监听,测试客户端是否连接到服务器,这里使用of
建立一个房间
这里可以参照官方的实例(不过是非跨域的):https://socket.io/get-started/chat
如果想要处理跨域可参考:https://socket.io/docs/v4/handling-cors/#cors-header-access-control-allow-origin-missing
io.of('my-chatroom').on('connection', (socket)=>{
console.log('有新用户连接了');
})
//服务器本地主机的数字 注意这里不是app
server.listen(3007, function(){
console.log("http:127.0.0.1:3007 启动了。。。");
})
复制代码
前端测试页面:(这里的socket.io.js
是从node_module
中拿出来的)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./js/socket.io.js"></script>
<title>Document</title>
</head>
<body>
<script>
socket = io('http://127.0.0.1:3007/my-chatroom', {
// 用于通知服务器在真正的请求中会采用chat-room请求头
extraHeaders: {
"chat-room": "123456789"
},
});
</script>
</body>
</html>
复制代码
然后使用 nodemon 运行 main.js, 并且使用live server
运行前端测试的 html 页面,可以看到终端中服务器和客户端已经通过 socketIO 连接了。
本次使用的数据没有写入在数据库或其他文件内,而是直接定义的。数据格式基本如下所示:
// 数据格式
"userList": [
// 用户
{
"name": "张三", // 姓名
"password": "123", // 密码
"online": false, // 是否在线
"nowSocketId": null, // 当前的socketid
"headPortrait": "http://127.0.0.1:3007/img/cherry.png", // 头像
// 聊天信息
"infos": [
// 聊天对象
{
"name": "李四", // 对象名
// 最后一次信息
"lastMsg": {
"msg": "这是最后一次了",
"time": 78495
},
// 所有信息
"allMsg": [
{
"msg": "这是最后一次了",
"time": 78495
}
]
},
]
},
]
复制代码
同时为了避免代码过多,新建一个 routerGet.js 来应对 http 访问的请求,前端访问的 get 请求方法也是使用 promise 自定义的
利用promise封装一个ajax,可以使用then返回数据
function getAjaxNew(config) {
let promise = new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
let readystatechange = ()=>{
if(req.readyState === 4) { //判断响应状态是否成功
let responseHeaders = req.getAllResponseHeaders(); //获取响应头信息
let data = req.responseText; //获取响应数据
// 数据处理
resolve(req.response);
}
}
req.onreadystatechange = readystatechange;
if (config.dataType)
req.responseType = config.dataType;
if (config.method == 'GET') {
if (config.options) {
req.open(config.method, config.url + '?' + config.options.join('&'), true); // true代表异步
}
else
req.open(config.method, config.url , true); // true代表异步
req.send();//发送请求
} else {
req.open(config.method, config.url, true); // true代表异步
if (config.options) {
let data = JSON.stringify(config.options);
req.send(data);//发送请求
} else{
req.send();//发送请求
}
}
})
return promise;
}
复制代码
如果登录成功,再获取用户列表,并且返回用户列表信息时为了安全性,需要将列表中一些关键数据比如 password 等进行隐藏,置空。
// 隐藏关键信息
function dataDeal(arr, myName) {
arr = JSON.parse(JSON.stringify(arr));
for(let i = 0; i <arr.length; i++) {
let item = arr[i];
item.password = null;
for(let p of item.infos) {
p.allMsg = null;
}
if (myName == item.name) {
arr.splice(i, 1)
}
}
return arr;
}
复制代码
在 socketIO 的后台方法中,介绍一下消息的收发
后台通过socket.on
接收前端传来的请求,并且通过socket.emit
来发送数据给请求者
登录或者离线,那么就要发送给除自己之外的用户,可以使用.broadcast()
来进行广播消息
发送给指定的用户,可以使用.to()
方法,传入的参数是指定用户的 socketid
更多方法可以查看官方 API:https://socket.io/docs/v4/server-api/或者书栈上的中文文档: https://www.bookstack.cn/read/veaba-socket.io-docs/README.md
io.of('my-chatroom').on('connection', (socket)=>{
// 当有用户登录时
socket.on('login', (info)=>{
// uName:登录者的名称 needToEmit:是否需要通知其他用户
let uName = info.auth.name, needToEmit = false;
......
// 通知其他用户谁登录了 broadcast:除自己以外广播消息
socket.broadcast.emit('newUserLogin', {
newUser: uName
})
})
// 接收新消息,存入数据,发送给需要提醒的某人
socket.on('chatSend', (data)=>{
......
// 如果键存在,则发送消息
if (anotherid) {
// to 发送给指定socketid用户
socket.to(anotherid).emit('hasNewMsg', {
originName: data.myName,
time: data.time,
con: data.con
})
}
})
})
复制代码
效果:
登录和离线左侧的头像栏会改变颜色聊天的话也会时时进行
总结
其实这个练习不足之处还是挺多的,没有把数据保存到下来,并且一些安全性问题和一些交互问题上其实也有待考虑。但是大致上使用 socketIO 做好了,因为本次目的还是为了学习 socketIO
评论