当前位置: 首页 > 后端技术 > Node.js

使用socket.io实时推送消息

时间:2023-04-03 12:49:30 Node.js

项目背景介绍我最近写的项目中有一个社交模块,需要实现这样一个功能:当用户被点赞、评论、关注等时,服务器需要实时向用户推送消息。最终完成的项目地址为:socket-message-push。下面我介绍一下实现思路和一些代码。项目过程中有几个对象:用Java实现的后台服务器,用Node实现的消息推送服务器。它将处理消息并将其发送到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',/**/);//向所有连接的套接字发出事件socket.on('reply',function(){/**/});//监听事件});关于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(路径.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(`监听端口:${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监听连接事件,当客户端与服务器建立连接,会调用这里的回调函数(客户端中的代码将在下一节介绍)。该函数的参数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事件,服务端收到后会做如下处理:信息;addSocketId(用户,{tokenId,socketId,userId});});addSocketId()会将用户信息添加到users数组中,不同的用户通过tokenId来区分,每个用户都有一个socketIds数组,里面保存了多个可能存在的socketId。该功能的具体代码可以在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');//或使用importsyntaximportiofrom'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的事件});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":"你不能通过!!!”}此时,如果一切顺利,你应该能够在客户端的控制台中看到收到的消息。您可以打开多个客户端页面,输入不同的tokenId,并检查消息是否发送给了正确的用户。参考https://github.com/socketio/s...https://socket.io/docs/这篇文章原文地址在我的博客:使用socket.io实时推送消息