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('有新用户连接了');})//服务器本地主机的数字 注意这里不是appserver.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
评论