SocketIO の 聊天练习

Posted 空城机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SocketIO の 聊天练习相关的知识,希望对你有一定的参考价值。

socketIO

socketIO概念

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

与websocket关系

websocket出现之前,客户端和服务器之间的即时通信往往依赖于客户端进行轮询操作,websocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,同时也降低了服务器的性能消耗。但是,websocket并不能兼容所有的浏览器,所以socketIO是不仅包含了websocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JFH9JasL-1653009651269)(https://bbs-img.huaweicloud.com/blogs/img/20220520/1653008134494176969.png)]


聊天练习结构

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

  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

以上是关于SocketIO の 聊天练习的主要内容,如果未能解决你的问题,请参考以下文章

SocketIO の 聊天练习

SocketIO + Flask 检测断开连接

设计模式のMediatorPattern(中介者模式)----行为模式

用React, Flask和SocketIO搭建在线聊天室

如何在nodejs服务器上使用android socketio模块在两个人之间进行聊天?

socket io:如何传递参数来断开套接字事件?