当前位置: 首页 > 科技观察

使用socket.io实时推送消息

时间:2023-03-12 23:02:38 科技观察

项目背景介绍最近写的项目中有一个社交模块,需要实现这样一个功能:当用户被点赞、评论、关注等时,服务端需要实时向用户推送消息。最终完成的项目地址为:socket-message-push。下面我介绍一下实现思路和一些代码。项目流程中有几个对象:Java实现的后端服务器Node.js实现的消息推送服务器用户操作的客户端事件处理流程如下:会处理并发送一条消息到Node.js消息推送服务器。Node.js消息推送服务器接收到后端发送的消息后,会对数据进行处理,确定将消息推送给哪个用户。用户客户端收到Node.js发送的消息,服务端推送消息后,即可显示通知。在上述过程中,Java后端服务器是如何实现的,不在本文讨论范围之内。本文将主要介绍如何使用Node.js实现消息推送服务器。考虑到消息推送服务器上必须记录当前在线用户的信息,才能将消息推送给特定的用户。所以当用户登录时,他们必须将他们的用户信息发送到Node.js服务器。为了实现这种双向实时消息传递,显然要考虑使用WebSocket来实现。现在我们正在使用Node.js作为我们的消息服务器,我们有一个方便的选择:socket.io。Socket.io简介Socket.io是一个用JavaScript实现的实时双向通信库。使用它来实现我们的功能会非常简单。socket.io由两部分组成:服务器(server):运行在Node.js服务器上客户端(client):运行在浏览器中。可以看到socket.io的示例代码如下,给出了socket.io发送监听事件的基本用法:io.on('connection',function(socket){socket.emit('request',/**/);//emitaneventtothesocketio.emit('broadcast',/**/);//emitaneventtoallconnectedsocketssocket.on('reply',function(){/**/});//listentotheevent});关于Socket.io还需要注意一件事:Socket.io不完全是WebSocket的实现。注意:Socket.IO不是WebSocket实现。尽管Socket.IO在可能的情况下确实使用WebSocket作为传输,但它会向每个数据包添加一些元数据:数据包类型、名称空间和需要消息确认时的ackid。接下来,我们需要使用Express.js创建一个服务器端程序,并在其中引入Socket.io。搭建Node.js服务器使用Express.js搭建基础服务器我们使用Express.js搭建Node.js消息推送服务器,先用一个简单的例子浏览一下它的功能://server.jsconstexpress=require('express');constapp=express();constpath=require('path');consthttp=require('http').Server(app);constport=4001;app.use(express.static(path.join(__dirname,'public')));app.get('/',function(req,res){res.sendFile(__dirname+'/public/index.html');});app.get('/api',function(req,res){res.send('.');});http.listen(port,function(){console.log(`listeningonport:${port}`);});将以上代码保存为server.js,新建一个public文件夹,将index.html文件放入其中。运行以下命令:nodeserver.js现在你可以在localhost:4001上看到效果了。介绍Socket.io现在我们有了一个基本的Express服务器,我们需要向它添加Socket.io。constio=require('socket.io')(http);io.on('connection',function(socket){console.log('auserconnected');socket.broadcast.emit('new_user',{});}这里的io监听连接事件,当client与server建立连接时,会调用这里的回调函数(client中的代码会在下一节介绍),该函数的参数socket表示客户端和服务器之间的当前连接。此连接的。建立的socket连接可以在客户端程序中打印出来,如下图所示:id属性可以用来标识这个连接,这样服务器就可以给特定的用户发送消息了。socket.broadcast.emit('new_user',{});这行代码表明套接字将向所有已与服务器建立连接的客户端(不包括自身)广播一条名为new_user的消息。后端推送消息的处理流程在Node服务器上建立了用户信息和socketid的映射表,因为同一个用户可能打开了多个页面,所以他的socketid可能有多个值。当用户连接时,为其添加一个值;当用户断开连接时,删除相应的值。Java后台有消息推送时,会向Node服务器的/api路径推送一条消息,包括tokenId等用于识别用户的数据。Node服务器收到post请求后,对请求内容进行处理。根据tokenId找出用户对应的socketid,socket.io会根据id推送消息给用户。为了方便处理用户信息,这里只用一个数组保存用户信息,实际工作中可以根据需要存入数据库。global.users=[];//记录登录用户的tokenId和socketId。当用户登录时,客户端会向服务端发送user_login事件,服务端收到后进行如下操作:socket.on('user_login',function(info){const{tokenId,userId,socketId}=info;addSocketId(users,{tokenId,socketId,userId});});addSocketId()会将用户信息添加到users数组中,不同的用户通过tokenId来区分,每个用户都有一个socketIds数组,里面保存了多个socketIds可能存在。该功能的具体代码可以在src/utils.js文件中找到。同样,还有一个删除用户信息的deleteSocketId()函数,代码见同文件。获取到用户的tokenId后,需要找到对应的socketId,然后向具体用户推送消息。//只向id=socketId的连接发送消息io.sockets.to(socketId).emit('receive_message',{entityType,data});服务端的思路大致相同,接下来介绍如何在客户端进行相应的处理。客户端Socket.io的初始化首先在html文件中引入Socket.io的客户端文件,比如通过CDN:其他引入方法:constio=require('socket.io-client');//或withimportsyntaximportiofrom'socket.io-client';导入Socket.io后得到io函数,用于与消息推送服务器建立连接。//假设你部署Node服务器后的地址是:https://www.example.com/ws//那么:WS_HOST='https://www.example.com'constmsgSocket=io(`${WS_HOST}`,{secure:true,path:'/ws/socket.io'});如果在本地监听:constmsgSocket=io('http://localhost:4001');这里如果写成io('https://www.example.com/ws')会报错,需要把/ws写进路径。为了在其他文件中使用这个变量,可以将msgSocket作为一个全局变量:window.msgSocket=msgSocket;用户建立连接//当用户登录时,将用户的信息发送给服务器。服务器收到信息后会建立套接字-用户映射关系。msgSocket.emit('user_login',{userId,socketId:msgSocket.id,tokenId});收到推送消息后处理//WebSocket连接建立后,监听名为receive_message的事件msgSocket.on('receive_message',msg=>{store.dispatch({type:'NEW_SOCKET_MSG',payload:msg});});WebSocket服务端向客户端推送消息后,客户端需要监听receive_message事件,并有相应的参数接收处理后的信息。由于Redux用于数据处理,因此在这里调度NEW_SOCKET_MSG操作,然后是正常的redux处理流程。在开发环境使用GitHub上的项目地址:socket-message-pushnpmrundev进行测试,现在你有一个运行在4001端口的消息推送服务器。但是没有后台服务器给我们发送消息,所以我们使用Postman来模拟发送消息。为了演示程序的功能,在项目的client文件夹下放了一个index.html文件。注意这个文件不能用在实际项目中,只是用来展示消息推送的效果。启动服务器后,打开client/index.html,根据提示输入一个tokenId。现在使用Postman向localhost:4001/api投递如下消息:{//tokens数组表示要向哪个用户推送消息"tokens":["1","2"],"data":"Youshallnotpass!!!"}此时,如果一切顺利,您应该能够在客户端的控制台中看到收到的消息。您可以打开多个客户端页面,输入不同的tokenId,并检查消息是否发送给了正确的用户。参考资料https://github.com/socketio/s...https://socket.io/docs/