写点什么

SocketIO の 聊天练习

作者:空城机
  • 2022 年 5 月 20 日
  • 本文字数:3216 字

    阅读完需:约 11 分钟

SocketIO の 聊天练习

socketIO

socketIO 概念

一个库,基于 Node.js 的实时应用程序框架。可以在浏览器和服务器之间实现实时,双向和基于事件的通信。它适用于每个平台、浏览器或设备,同样注重可靠性和速度。


与 websocket 关系

websocket出现之前,客户端和服务器之间的即时通信往往依赖于客户端进行轮询操作,websocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,同时也降低了服务器的性能消耗。但是,websocket并不能兼容所有的浏览器,所以socketIO是不仅包含了websocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口


聊天练习结构

基于 socketIO 的双向通信,准备制作一个聊天界面。


前端:聊天界面的大体样式参考于微信界面后台:使用node.js + socketIO

在动手之前,首先要规划一下需要有的功能(虽然很多是我自己后面想到再加的 🐶 )



项目步骤

  1. 首先使用yarn init创建一个项目

  2. 下载需要的 express 和 socket.io, 命令:yarn add socket.io express


  1. 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"],  // 被允许的请求头    },});
复制代码


  1. 在 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 连接了。


  1. 本次使用的数据没有写入在数据库或其他文件内,而是直接定义的。数据格式基本如下所示:

// 数据格式"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                    }                ]            },        ]    },]
复制代码


  1. 同时为了避免代码过多,新建一个 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;}
复制代码



  1. 在 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

发布于: 刚刚阅读数: 2
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
SocketIO の 聊天练习_socket_空城机_InfoQ写作社区