利用socket.io实现消息实时推送
最近在写的项目中存在着社交模块,需要实现这样的一个功能:当发生了用户被点赞、评论、关注等操作时,需要由服务器向用户实时地推送一条消息。最终完成的项目地址为:socket-message-push,这里将介绍一下实现的思路及部分代码。
项目背景介绍
最近在写的项目中存在着社交模块,需要实现这样的一个功能:当发生了用户被点赞、评论、关注等操作时,需要由服务器向用户实时地推送一条消息。最终完成的项目地址为: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', /* */); // emit an event to the socket
- io.emit('broadcast', /* */); // emit an event to all connected sockets
- socket.on('reply', function(){ /* */ }); // listen to the event
- });
关于 Socket.io 还有一点需要注意:Socke.io 并不完全是 WebSocket 的实现。
- Note: Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed.
接下来我们需要用 Express.js 来建立一个服务器端程序,并在其中引入 Socket.io。
Node.js 服务器的搭建
利用 Express.js 搭建基础服务器
我们使用了 Express.js 来搭建 Node.js 消息推送服务器,先利用一个简要的例子来浏览其功能:
- // server.js
- const express = require('express');
- const app = express();
- const path = require('path');
- const http = require('http').Server(app);
- const port = 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(`listening on port:${port}`);
- });
将上面的代码保存为 server.js,新建一个 public 文件夹,在其中放入 index.html 文件。运行以下命令:
- node server.js
现在即可在 localhost:4001 查看效果了。
引入 Socket.io
现在已经有了一个基础的 Express 服务器,接下来需要将 Socket.io 加入其中。
- const io = require('socket.io')(http);
- io.on('connection', function(socket) {
- console.log('a user connected');
- socket.broadcast.emit('new_user', {});
- }
这里的 io 监听 connection 事件,当 client 与 server 建立了连接之后,这里的回调函数会被调用(client 中的代码将在下一节介绍)。
函数的参数 socket 代表的是当前的 client 和 server 间建立的这个连接。可在 client 程序中将这个建立的 socket 连接打印出来,如下图所示:
其中的 id 属性可以用于标识出这一连接,从而 server 可以向特定的用户发送消息。
- socket.broadcast.emit('new_user', {});
这一行代码表示 socket 将向当前所有与 server 建立了连接的 client(不包括自己) 广播一条名为 new_user 的消息。
后端推送消息的处理流程
- 在 Node 服务器建立一个用户信息和 socket id 的映射表,因为同一用户可能打开了多个页面,所以他的 socket id 可能存在多个值。当用户建立连接时,往其中添加值;用户断开连接后,删除相应值。
- 当 Java 后台存在需要推送的消息时,会向 Node 服务器的 /api 路径 post 一条消息,其中包括用于标识用户的 tokenId 和其它数据。
- Node 服务器接收到 post 请求后,对请求内容进行处理。根据 tokenId 找出与该用户对应的 socket id,socket.io 会根据 id 来向用户推送消息。
对用户信息的处理
方便起见,这里只用一个数组保存用户信息,实际工作中可以根据需要放入数据库中保存。
- global.users = []; // 记录下登录用户的tokenId, socketId
当用户登录时,client 会向 server 发送 user_login 事件,服务器接收到后会做如下操作:
- socket.on('user_login', function(info) {
- const { tokenId, userId, socketId } = info;
- addSocketId(users, { 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 的 client 端文件,例如通过 CDN 引入:
- <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
其它的引入方式:
- <script src="/socket.io/socket.io.js"></script>
- const io = require('socket.io-client');
- // or with import syntax
- import io from 'socket.io-client';
引入 Socket.io 后就获得了 io 函数,通过它来与消息推送服务器建立连接。
- // 假设你将 Node 服务器部署后的地址为:https://www.example.com/ws
- // 则: WS_HOST = 'https://www.example.com'
- const msgSocket = io(`${WS_HOST}`, {
- secure: true,
- path: '/ws/socket.io'
- });
如果监听本地:
- const msgSocket = io('http://localhost:4001');
这里如果写成 io('https://www.example.com/ws') 会出现错误,需要将 /ws 写入path中。
为了能在其它文件使用这一变量,可将 msgSocket 作为一个全局变量:
- window.msgSocket = msgSocket;
用户建立连接
- // 用户登录时,向服务器发送用户的信息。服务器会在收到信息后建立 socket 与用户的映射。
- 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 进行数据的处理,所以这里 dispatch 了一个 NEW_SOCKET_MSG action,后续则是常规的 redux 处理流程了。
项目的使用
GitHub 上的项目地址:socket-message-push
- npm run dev
即可在 devlopment 环境下进行测试,现在你就有了一个运行在4001端口的消息推送服务器了。
但是这里并没有后端的服务器来向我们发送消息,所以我们将利用 Postman 来模拟发送消息。
为了展示程序的功能,在项目的 client 文件夹下放置了一个 index.html 文件。注意这个文件并不能用在实际的项目中,只是用来显示消息推送的效果而已。
在开启了服务器之后,打开 client/index.html,根据提示随意输入一个 tokenId 即可。
现在利用 Postman 向 localhost:4001/api post 如下的一条信息:
- {
- // tokens 数组表示你想向哪个用户推送消息
- "tokens": ["1", "2"],
- "data": "You shall not pass!!!"
- }
至此,如果一切顺利,你应该能够在 client 的控制台中看到收到的消息了。
你可以打开多个 client 页面,输入不同的 tokenId,然后检查消息是否发送给了正确的用户。
参考资料
利用socket.io实现消息实时推送的更多相关文章
- 用node.js(socket.io)实现数据实时推送
在做商品拍卖的时候,要求在商品的拍卖页面需要实时的更新当前商品的最高价格.实现的方式有很多,比如: 1.setInterval每隔n秒去异步拉取数据(缺点:更新不够实时) 2. AJAX轮询方式方式推 ...
- 基于HTTP协议之WEB消息实时推送技术原理及实现
很早就想写一些关于网页消息实时推送技术方面的文章,但是由于最近实在忙,没有时间去写文章.本文主要讲解基于 HTTP1.1 协议的 WEB 推送的技术原理及实现.本人曾经在工作的时候也有做过一些用到网页 ...
- dwr3+spring实现消息实时推送
最近项目要实现一个消息推送的功能,主要就是发送站内信或者系统主动推送消息给当前在线的用户.每次的消息内容保存数据库,方便用户下次登录后也能看到.如果当前用户在线,收到站内信就主动弹出提示.一开始想到的 ...
- WebSocket实现站内消息实时推送
关于WebSocket WebSocket是HTML5 开始提供的一种在单个TCP连接上进行全双工通讯的协议.什么是全双工?就是在同一时间可以发送和接收消息,实现双向通信,比如打电话.WebSocke ...
- Asp.net SignalR 实现服务端消息实时推送到所有Web端
ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.实际上 Asp.net SignalR 2 实现 服务端消息推送到Web端, 更加 ...
- 基于swoole+Redis的消息实时推送通知
swoole+Redis将实时数据的推送 一 实现功能 设计师订单如果设计师未抢单,超时(5分钟)设计订单时时给设计师派送, 设计师公众号中收到派单信息 设计发布者收到派单成功信息 环境 centos ...
- php 消息实时推送(反ajax推送)
入口文件index.html <!DOCTYPE HTML> <html> <head> <title>反ajax推送</title> &l ...
- nodejs+socketio+redis实现前端消息实时推送
1. 后端部分 发送redis消息 可以参考此篇实现(直接使用Jedis即可) http://www.cnblogs.com/binyue/p/4763352.html 2.后端部分: 接收redis ...
- Channels集成到Django消息实时推送
channel架构图 InterFace Server:负责对协议进行解析,将不同的协议分发到不同的Channel Channel Layer:频道层,可以是一个FIFO队列,通常使用Redis Dj ...
随机推荐
- [JZOJ3106]锻炼
题目大意: 给你一个$n\times m(n,m\leq 50)$的网格图,其中有一个四连通的障碍物.给定起点和终点,每次你可以走到和当前位置八连通的一个方格内,问绕障碍物一圈最短要走几格? 思路: ...
- JSP内置对象阶段案例
1.login2: <%@ page language="java" import="java.util.*" pageEncoding="UT ...
- HTTP抓包工具Fiddler
http://blog.csdn.net/kobejayandy/article/details/16908261
- 模拟Spring中的getBean方法
一直知道Spring是运用反射技术的,但具体怎么用呢?今天就模拟下getBean方法. 步骤: 1.用Dom4j解析xml配置文件,取出我们需要的信息 2.遍历Bean节点,根据每个Bean节点的cl ...
- kubernetes监控--Prometheus
本文基于kubernetes 1.5.2版本编写 kube-state-metrics kubectl create ns monitoring kubectl create sa -n monito ...
- python装饰器的作用
常见装饰器:内置装饰器:类装饰器.函数装饰器.带参数的函数装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常 ...
- Playonlinux
apt-get install playonlinux -y apt-get install winbind -y apt-get install unzip -y 开始中搜索:playonlinux ...
- linux bash 入门
#!/bin/bash #shell使用的熟练成都反映用户对Unix/Linux使用的熟练程度 #shell 有两种执行命令的方式:交互式和批处理 #常见的shell脚本解释器有bash,sh,csh ...
- crontab配置
1.命令功能 通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本.时间间隔的单位可以是分钟.小时.日.月.周及以上的任意组合.这个命令非常适合周期性的 ...
- 关于ng-router嵌套使用和总结
那是某个下午的review代码的过程.js中有一段html,像是这样. var html = '<div>...此处还有很多html代码....</div>' 我的同事想我提出 ...