nodejs+socket.io实现p2p消息实时发送的项目实践

目录
  • 常见的消息通知:
  • 实现思路与步骤等
  • 其他方法介绍
  • 技术实现与相关包介绍
    • 包介绍
    • 技术实现
      • 服务端
      • 客户端

常见的消息通知:

常见的站内通知类别(括号里是对自己目前项目出现情况的分析,读者忽略):

  • 公告 Announcement (通道加入新的组织、某组织或用户新上传了某数据摘要、系统凌晨需要版本更新等事件)
  • 提醒 Remind(用户之间、系统与用户之间)
    • 资源订阅提醒(关注的数据摘要更新了内容、评论等)
    • 资源发布提醒(我发布的数据摘要被评论了,被关注了,被申请交易了)
    • 系统提醒
  • 私信 Mailbox(类似聊天室吧,暂时没有这需求)

实现思路与步骤等

除了用消息队列MQ以外,笔者想到的是使用websocket协议实现,该协议为全双工通信full-duplex,长连接PersistentConnection,相比http来说是种持久化协议。

其中主要的开发步骤有:

  • 绑定连接(用户账号和websocket之间的连接)
  • 管理连接
  • 收发消息(数据格式和读取等具体实现)。

其中,需要注意的点有:

  • 长连接的心跳激活处理;
  • 服务端调优实现高并发量client同时在线(单机服务器可以实现百万并发长连接);
  • 群发消息;
  • 服务端维持多用户的状态;
  • 从WebSocket中获取HttpSession进行用户相关操作等

具体实现思路:

  • 前端使用WebSocket与服务端创建连接的时候,将用户ID传给服务端,服务端将用户ID与channel关联起来存储,同时将channel放入到channel组中。(这里的channel就是服务器与客户端之间的连接)
  • 如果需要给所有用户发送消息,直接执行channel组的writeAndFlush()方法;
  • 如果需要给指定用户发送消息,根据用户ID查询到对应的channel,然后执行writeAndFlush()方法;
  • 前端获取到服务端推送的消息之后,将消息内容展示到文本域中。

其他方法介绍

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 优点:后端程序编写比较容易。 缺点:请求中有大半是无用,浪费带宽和服务器资源。 实例:适于小型应用。

长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。 优点:在无消息的情况下不会频繁的请求,耗费资小。 缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。 Comet异步的ashx, 实例:WebQQ、Hi网页版、Facebook IM。

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。 优点:消息即时到达,不发无用请求;管理起来也相对便。 缺点:服务器维护一个长连接会增加开销。 实例:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。 优点:实现真正的即时通信,而不是伪即时。 缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。 实例:网络互动游戏。

技术实现与相关包介绍

包介绍

nodejs不像其他的服务器,对于不同的连接,不支持进程和线程操作,写这类功能的时候就需要找更合适的包。

使用WebSocket协议的包有好多,这里我先讲一种常用的包是nodejs-websocket包,网评说使用较为繁琐,这里就没使用。它需要依赖于底层的C++,Python的环境,支持以node做客户端的访问。当然了,这里我一定要说一下,nodejs-websocket是纯粹的使用了WebSocket协议,因此使用时需要写心跳检测,检测用户是否在线等情况。

我采用的是socket.io,它使用起来较为简单,功能强大,支持集成websocket服务器端和Express3框架与一身。它可以不需要心跳检测,不过这也是个相对说法,因为它结合封装了轮询机制和实时通信,当websocket连接断掉时,它会不停的尝试连接,耗费资源。当然了,还有其他库,比如node-websocket-server(不需要了解,直接放弃)。

技术实现

在实现前,考虑到发送消息时,向指定用户发送WebSocket消息,但对方可能不在线,这种情况,我这么处理:

  • 如果接收者在线,则存储进redis并实时发送消息;
  • 否则将消息存储到redis,等用户登陆上线后主动推送未读消息。

socket.io的客户端和服务端都有两个函数 on()、emit(),核心函数,可轻松实现客户端与服务端的双向通信。

  • emit:触发一个事件,第一个参数是事件名称,第二个参数是要发送到另一端的数据,第三个参数是一个回调函数用来确认对方的接收信息(也可以说时回执),可忽略。

    • socket.emit 信息传输对象为当前 socket 对应的 client ,各个client socket 相互不影响。
    • socket.broadcast.emit 信息传输对象为所有 client ,排除当前socket 对应的 client。
    • io.sockets.emit信息传输对象为所有 client。
  • on:注册一个事件,用来监听 emit 触发的事件。

服务端

直接上代码:

    'use strict';

    // 维护socket连接的代码
    const { addSocketId, getSocketId, deleteSocketId } = require('../../../utils/socket/socketId');
    // 保存消息
    const message = require('../saveMessage');
    // socket连接许可验证
    const { socketAuth } = require('../../../middleware/socket/index')

    // socket接口,传入/bin/www.js
    function init(io) {

    /**
     * @description: 为每个传入执行的功能Socket,并且接收套接字和可选地将执行延迟到下一个注册的中间件的参数
     */
    io.use((socket, next) => {
        if (socket.request.headers.cookie) return next();
        next(new Error('Authentication error'));
    });

    io.on('connection', function(socket) {

        /**
         * @description: 用户登录,则保存用户连接的相关信息,并从redis拉取未读消息,推送给该用户
         */
        socket.on('user_login', function(socketInfo) {
            if(!socketInfo.userId) {
                // io.sockets.to(socketInfo['socketId']).emit('disconnect', '');
                return;
            }
            // 将用户与socket插入数据库中
            addSocketId(socketInfo);  

            if (process.env.NODE_ENV === 'development') {
                displayUserInfo(socketInfo);
            };

            // 推送所有消息
            message.pushMessage(socketInfo['userId']).then(pushData => {
                io.sockets.to(socketInfo['socketId']).emit('push_message', pushData);
            });
        });

        /**
         * @description: 发给某用户交易通知(在线实时通知,并存储至redis)
         */
        socket.on('todo', function(todoData) {
            // 存入redis
            message.addMessage(todoData);
            // 检测用户是否在线
            message.isOnline(todoData['receiver_id']).then(isOnline => {
                // 用户在线则通信
                if (isOnline == true) {
                    getSocketId(todoData['receiver_id']).then(socketId => {
                        io.sockets.to(socketId).emit('todo_message', todoData);
                    });
                };
            });
        });

        // TODO: 需要提醒前端在关闭窗口之前先断开连接(窗口刷新之前应该不需要)
        /**
         * @description: 断开连接
         */
        socket.on('disconnect', function() {
            // 从数据库中删除连接
            deleteSocketId(socket.id);
            // 判断当前是否是开发环境
            if (process.env.NODE_ENV === 'development') {
                displayUserInfo();
            }
        });

    });

}

function displayUserInfo(user) {
    console.log(`当前登录用户信息:${user}`);
    return;
}

module.exports = {
    init
};

上方代码中,主要创建了connection事件,其下又有user_login、todo、disconnect事件,然后这些事件下又有其创建或监听的事件。其中,user_login事件主要是监听前端用户的登录成功,若用户成功上线,则将redis内的已读未读消息分类后推送给客户端。todo事件则是判断用户在线后,实时传递消息,需要注意使用io.sockets.to(socketId).emit(eventname, eventdata)实现P2P消息传送,socketId即为接收消息用户的WebSocket连接的ID。客户端则需要监听后面emit()参数中的eventname事件。disconnect事件则是在客户端用户登出或刷新页面等认为是断开WebSocket连接时,在维护的socket连接组中删除该用户的WebSocket连接信息。

当然,在连接到connection事件前,有一个中间件io.use((socket, next) => {},是判断对方的连接是否有效(带有cookie的主动连接)。

然后,在/bin/www .js中引入io:

#!/usr/bin/env node

// 模块依赖
var app = require('../app');
var http = require('http');
const socketIndex = require('../src/routes/socket/index/socket');

// 从环境中取端口,应用到express
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

// 创建http服务(将express注册到http中)
server = http.createServer(app);

// 监听
var io = require('socket.io')(server, {
  cors: {
      origin: '*'
  }
  // path: '/socket' // 重新定义socket连接路径
});

// 全局声明
global.io = io;

// socket的程序文件下引入io
socketIndex.init(io);

其中,引入函数init()即是上一段代码中的init函数,传入参数即为在服务端入口中创建的io服务。io服务中需要传入cors参数,解决跨域问题,如果想更改websocket连接的地址,则使用path参数,其参数值即是在原先基础的websocket连接地址后加上。

客户端

首先创建一个socket对象,io() 的第一个参数是链接服务器的 URL,默认情况下是 window.location(需要修改成服务端的URL,包括对应的模块或权限对应的指定路径,path参数)。

到此这篇关于nodejs+socket.io实现p2p消息实时发送的项目实践的文章就介绍到这了,更多相关node socket.io实现p2p实时发送内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • node.js中Socket.IO的进阶使用技巧

    在上一篇博文Socket.IO中,我简要介绍了Socket.IO的基本使用方法并创建了一个简单的聊天室DEMO.本篇在入门篇的基础上,继续探讨Socket.IO的进阶用法.本篇将从配置.房间.事件等方面入手,介绍一些Socket.IO中实用的API和注意事项. 1. 配置 Socket.IO提供了4个配置的API:io.configure, io.set, io.enable, io.disable.其中io.set对单项进行设置,io.enable和io.disable用于单项设置布尔型的配置

  • node.js中的Socket.IO使用实例

    1. 简介 首先是Socket.IO的官方网站:http://socket.io 官网非常简洁,甚至没有API文档,只有一个简单的"How to use"可以参考.因为Socket.IO就跟官网一样简洁好用易上手. 那么Socket.IO到底是什么呢?Socket.IO是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用.它会自动根据浏览器从WebSocket.AJAX长轮询.Iframe流等等各种方式中选择最

  • node.js基于socket.io快速实现一个实时通讯应用

    随着web技术的发展,使用场景和需求也越来越复杂,客户端不再满足于简单的请求得到状态的需求.实时通讯越来越多应用于各个领域. HTTP是最常用的客户端与服务端的通信技术,但是HTTP通信只能由客户端发起,无法及时获取服务端的数据改变.只能依靠定期轮询来获取最新的状态.时效性无法保证,同时更多的请求也会增加服务器的负担. WebSocket技术应运而生. WebSocket概念 不同于HTTP半双工协议,WebSocket是基于TCP 连接的全双工协议,支持客户端服务端双向通信. WebSocke

  • 基于socket.io和node.js搭建即时通信系统

    使用socket.io和nodejs搭建websocket服务器端 socket.io不仅可以搭建客户端的websocket服务,而且支持nodejs服务器端的websocket. 下面让我来介绍一下怎么安装配置nodejs. 进入http://nodejs.org/#download下载msi文件.一直点next安装.最后文件会自动安装在C:\nodejs目录下. 安装完成后,会自动配置环境环境变量.如果没有自动配置,自己手动在path处加上 ;C:\nodejs\. 安装完成后,需要配置np

  • nodejs结合Socket.IO实现的即时通讯功能详解

    本文实例讲述了nodejs结合Socket.IO实现的即时通讯功能.分享给大家供大家参考,具体如下: 动态web 在html5以前,web的设计上并没有考虑过动态,他一直是围绕着文档设计的,我们看以前比较老的网站,基本上都是某一刻用来显示单一的文档的,用户请求一次web页面,获取一个页面,但是随着时间的推移,人们想要web做更多的事情了,而不是简单的要显示文档,而javaScript一直处于开发人员推动web页面功能的发展中心. Ajax无疑是动态Web页面的一个重大发展,他不再需要我们即使更新

  • node.js中使用socket.io制作命名空间

    如果开发者想在一个特定的应用程序中完全控制消息与事件的发送,只需要使用一个默认的"/"命名空间就足够了.但是如果开发者需要将应用程序作为第三方服务提供给其他应用程序,则需要为一个用于与客户端连接的socket端口定义一个独立的命名空间. io.of(namespace) 制作两个命名空间 chat和news然后在客户端相互发送信息. 复制代码 代码如下: var express=require("express"); var http=require("h

  • node中socket.io的事件使用详解

    socket.io类库不但可以相互发送消息,而且还可以通过socket端口对象的emit方法互相发送事件. emit在之前的事件上说过现在一句话带过:emit是用来手动触发事件的. 复制代码 代码如下: socket.emit(event,data,function(data1,data2......){ }); 在使用emit方法发送事件时,可以再另一端使用socket端口对象的on方法会once方法监听. 复制代码 代码如下: socket.on(event,function(data,fn

  • node.js中的socket.io入门实例

    关于websocket等反向ajax技术介绍 在实时web应用中,常见的方法是反向Ajax.反向Ajax的定义: 反向Ajax(Reverse Ajax)本质上则是这样的一种概念:能够从服务器端向客户端发送数据.在一个标准的HTTP Ajax请求中,数据是发送给服务器端的,反向Ajax可以某些特定的方式来模拟发出一个Ajax请求,这些方式本文都会论及,这样的话,服务器就可以尽可能快地向客户端发送事件(低延迟通信). 反向Ajax技术主要有两点内容:一是服务器端保持住TCP连接直到其有数据发送给客

  • node.js中的socket.io的广播消息

    在多个客户端与服务器端建立连接后,socket.io()服务器具有一个sockets属性,属性值为所有与客户端建立连接的socket对象.可以利用该对象的send方法或emit方法向所有客户端广播消息. io.sockets.send("user commected); io.socket.emit("login",names); 案例 server.js代码: 复制代码 代码如下: var express=require("express"); var

  • nodejs+socket.io实现p2p消息实时发送的项目实践

    目录 常见的消息通知: 实现思路与步骤等 其他方法介绍 技术实现与相关包介绍 包介绍 技术实现 服务端 客户端 常见的消息通知: 常见的站内通知类别(括号里是对自己目前项目出现情况的分析,读者忽略): 公告 Announcement (通道加入新的组织.某组织或用户新上传了某数据摘要.系统凌晨需要版本更新等事件) 提醒 Remind(用户之间.系统与用户之间) 资源订阅提醒(关注的数据摘要更新了内容.评论等) 资源发布提醒(我发布的数据摘要被评论了,被关注了,被申请交易了) 系统提醒 私信 Ma

  • Nodejs+Socket.io实现通讯实例代码

    目录结构 D:. │ package.json │ server.js │ └─public index.html socket.io.js 需要的条件 socket.io.js 供前端界面初始化io socket.io 供NodeJs端提供socket方法 socket.io.js存在于socket.io-client socket.io存在于socket.io 演示的功能 客户端发送消息给服务端 后端触发事件告知客户端 客户端离开触发服务端事件 服务端 server.js var expre

  • nodejs结合socket.io实现websocket通信功能的方法

    本文实例讲述了nodejs结合socket.io实现websocket通信功能的方法.分享给大家供大家参考,具体如下: 因为项目中有需要实时获取后台数据的场景,之前一直是使用http心跳请求的方法.因为websocket与此模式相比有很大的性能提升,而且可以提高实时性,所以对websocket作了一些研究.这里是使用nodejs+socket.io来实现的. 达成目标 将原来心跳请求后台数据的方式,修改为通过socket连接后台统一推送的方式.后台的数据由别的进程写入文件或写入redis,这里实

  • vue+node+socket io实现多人互动并发布上线全流程

    一.背景 1. 前端使用vue + vuex + socket.io-client npm install socket.io-client --save-dev 2. 后端使用node + express + socketio 1.搭建node开发环境 npm init -y 安装所需依赖 npm install express --save-dev npm install socket.io-client --save-dev 二. socket.io相关用法概览 1. 发送事件 socke

  • 利用Socket.io 实现消息实时推送功能

    项目背景介绍 最近在写的项目中存在着社交模块,需要实现这样的一个功能:当发生了用户被点赞.评论.关注等操作时,需要由服务器向用户实时地推送一条消息.最终完成的项目地址为:https://github.com/noiron/socket-message-push,这里将介绍一下实现的思路及部分代码. 项目的流程中存在着这样的几个对象: 用 Java 实现的后端服务器 用 Node.js 实现的消息推送服务器 用户进行操作的客户端 事件处理的流程如下: 用户进行点赞操作时,后端服务器会进行处理,并向

  • nodejs结合Socket.IO实现websocket即时通讯

    目录 为什么要用 websocket Socket.io 开源项目 效果预览 app.js index.html 为什么要用 websocket websocket 是一种网络通信协议,一般用来进行实时通信会使用到. websocket 协议和 http 协议类似,http 协议有一个缺陷,只能由客户方端发起请求,服务端根据请求 url 和传过去的参数返回对应结果 websocket 是双向通信的,只要 websocket 连接建立起来,可以由客户端给服务端发送数据,也可以由服务端主动给客户端发

随机推荐