离线消息如何实现?-- ESFramework 4.0 快速上手(02)

 在ESFramework 4.0 快速上手一文中,主要介绍了如何使用ESPlus.Rapid命名空间中的引擎来快速地构建基于TCP的网络通信系统,即使是使用ESPlus.Rapid来进行ESFramework快速开发,也还有很多可以介绍的内容,于是,我想再多写几篇文章来说明现实通信系统中的一些常见需求如何使用ESFramework快速实现。本文是为第一篇,介绍离线消息的原理和实现。

一.如何截获离线消息  

  阅读了ESFramework 4.0 快速上手朋友都知道,一个在线用户给另一个用户发送文本信息或二进制信息采用的是ESPlus.Application.CustomizeInfo.Passive.ICustomizeInfoOutter接口的Send方法,如发送二进制信息:

/// <summary>
        /// 向在线用户targetUserID发送二进制信息。如果目标用户不在线,则服务端会调用ICustomizeInfoBusinessHandler.OnTransmitFailed方法来通知应用程序。
        /// </summary>
        /// <param name="targetUserID">接收消息的目标用户ID</param>
        /// <param name="informationType">自定义信息类型</param>
        /// <param name="info">二进制信息</param>      
        void Send(string targetUserID, int informationType, byte[] info);

  对于类似在线用户发给其他用户的P2P类型的消息,可以通过P2P通道发送,也可以通过服务器中转。当目标用户不在线时,P2P通道肯定是不存在的,所以消息一定是提交到服务器。服务器接收到要转发的P2P消息时,判断目标用户是否在线,如果在线,则直接转发;否则,框架会回调ESPlus.Application.CustomizeInfo.Server.ICustomizeInfoBusinessHandler接口的OnTransmitFailed方法:

/// <summary>
        /// 当因为目标用户不在线而导致服务端转发自定义信息失败时,框架将调用此方法来通知应用程序。
        /// </summary>
        /// <param name="information">转发失败的信息</param>      
        void OnTransmitFailed(Information information);

我们只要实现ICustomizeInfoBusinessHandler接口的这个方法就可以监控到所有的离线消息了。

二.离线消息管理

  截获到离线消息后,我们可能需要将其存到数据库,然后,等到目标用户上线的时候,再从数据库中提取属于该用户的离线消息发送给他即可。

  首先,我们需要对离线消息做一个封装 -- OfflineMessage:

  接下来,我们定义IOfflineMessageManager接口,用于管理离线消息:

/// <summary>
    /// 离线消息管理器。
    /// </summary>
    public interface IOfflineMessageManager
    {
        /// <summary>
        /// 存储离线消息。
        /// </summary>       
        /// <param name="msg">要存储的离线消息</param>
        void Store(OfflineMessage msg);

/// <summary>
        /// 提取目标用户的所有离线消息。
        /// </summary>       
        /// <param name="destUserID">接收离线消息用户的ID</param>
        /// <returns>属于目标用户的离线消息列表,按时间升序排列</returns>
        List<OfflineMessage> Pickup(string destUserID);
    }

  实现这个接口,我们便可以将离线消息存储到数据库或文本或网络等等,然后等到需要时再次从中提取。

三.存储离线消息

  有了IOfflineMessageManager接口,我们便可以实现ICustomizeInfoBusinessHandler接口的OnTransmitFailed方法了:

private IOfflineMessageManager offlineMessageManager = ......;
        public void OnTransmitFailed(Information information)
        {
            OfflineMessage msg = new OfflineMessage(information.SourceID, information.DestID, information.InformationType, information.Content);
            this.offlineMessageManager.Store(msg);
        }

  我们也许并不需要将所有的离线消息都存储起来,有些不重要的离线消息可以丢弃,而只保存那些我们关心的消息。这只需要在存储消息之前加一个条件判断进行过滤即可。

四.提取并发送离线消息

  我们已经知道,可以通过IUserManager的SomeOneConnected事件来得知某个用户上线了,于是,我们可以在该事件处理函数中,提取属于该用户的离线消息并发送给他。我们通过类似下面的代码来做到这一点。

public class OfflineMessageBridge
    {
        #region UserManager
        private IUserManager userManager;
        public IUserManager UserManager
        {
            set { userManager = value; }
        } 
        #endregion

#region OfflineMessageManager
        private IOfflineMessageManager offlineMessageManager;
        public IOfflineMessageManager OfflineMessageManager
        {
            set { offlineMessageManager = value; }
        } 
        #endregion

#region CustomizeInfoController
        private ICustomizeInfoController customizeInfoController;
        public ICustomizeInfoController CustomizeInfoController
        {
            set { customizeInfoController = value; }
        } 
        #endregion

public void Initialize()
        {
            this.userManager.SomeOneConnected += new CbGeneric<ESFramework.Server.UserManagement.UserData>(userManager_SomeOneConnected);
        }

void userManager_SomeOneConnected(ESFramework.Server.UserManagement.UserData userData)
        {
            List<OfflineMessage> list = this.offlineMessageManager.Pickup(userData.UserID);
            if (list != null && list.Count > 0)
            {
                foreach (OfflineMessage msg in list)
                {
                    this.customizeInfoController.Send(msg.DestUserID, msg.InformationType, msg.Information);
                }
            }
        }
    }

  当用户上线时,会将属于他的离线消息按照时间的顺序一一发送给他。当然,你也可以将属于他的所有离线消息打成一个包,一次性发送也可以。如果是这样,你就需要再增加一条自定义的信息类型和相关的协议类了。

五.小结

  从上面可以看出,基于ESFramework实现离线消息策略是相当简单的,最主要的焦点有两个:第一是可以通过实现ICustomizeInfoBusinessHandler接口来截获到所有的离线消息;第二是通过IUserManager的SomeOneConnected事件就能知道用户上线的时刻。

  有的朋友可能会问离线文件又该怎么实现了?实际上也是同样的原理,只不过要多用到ESPlus.Application.FileTransfering命名空间下的一些类来完成文件的收发功能,这个等有时间的时候再来介绍。

  本文只是实现离线消息的一个简单示例,在实际的应用中,可能需要做更多的工作来满足项目的具体需要,这里就不再一一赘述了。

时间: 2024-07-30 22:28:11

离线消息如何实现?-- ESFramework 4.0 快速上手(02)的相关文章

ESFramework 4.0 快速上手(01) -- Rapid引擎

(在阅读该文之前,请先阅读 ESFramework 4.0 概述 ,会对本文的理解更有帮助.) ESFramework/ESPlatform 4.0 的终极目标是为百万级的用户同时在线提供支持,因为强大,所以使用也较为复杂,配置也较多.但是如果我们的应用只是一个中小型的通信应用(同时在线5000人以下),直接使用ESPlatform就有点显得杀鸡用牛刀了.ESPlus.Rapid提供了一种快速的方式,来解决类似中小型的通信应用,以最简洁的方式来使用ESFramework. 使用ESPlus.Ra

ESFramework 4.0 快速上手(06) -- Rapid引擎(续)

<ESFramework 4.0 快速上手>系列介绍的都是如何使用Rapid引擎(快速引擎) -- RapidServerEngine 和 RapidPassiveEngine.其实,大家可以将这两个引擎看作是两个壳,内部包装的才是真正的ESFramework的网络引擎, ESFramework支持很多种网络引擎(客户端/服务端.二进制协议/文本协议.TCP/UDP),而RapidServerEngine和RapidPassiveEngine采用的是基于TCP和二进制协议的服务端引擎和客户端引

如何使用自定义消息?--ESFramework 4.0 快速上手(04)

在ESFramework 4.0 快速上手一文中,我们讲述了如何使用Rapid引擎可以快速地上手ESFramework开发,文中介绍了使用ESPlus.Application.CustomizeInfo命名空间下的类可以发送和处理自定义消息,本文我们就通过一个简单的例子来深入讲解如何使用自定义消息. 例子的场景很简单:假设客户端登陆到服务器之后,要求请求加入某个组,服务端收到该请求后,处理该请求,并给客户端相应的回复 -- 是否加入成功,客户端收到回复后,即可作出相应的处理. 一.定义消息类型和

重登陆模式 --ESFramework 4.0 快速上手(07)

在ESFramework框架中基于TCP的服务端引擎(当然也包括Rapid引擎)都采用了这样一条规则:默认情况下,客户端与服务器成功建立TCP连接以后,服务端会从客户端发过来的第一条消息中取出消息头的UserID属性的值,并将其与对应的TCP连接绑定起来.这样,服务端就知道每一个TCP连接所对应的用户UserID,而当我们要求服务端向某个客户端发送消息时,服务端就知道通过哪个TCP连接进行发送了.TCP连接与UserID是一一对应的,一个TCP连接只能对应一个UserID,同样的,一个UserI

判定生死的心跳机制 --ESFramework 4.0 快速上手(07)

在Internet上采用TCP进行通信的系统,都会遇到一个令人头疼的问题,就是"掉线".而"TCP掉线"这个问题远比我们通常所能想象的要复杂的多 -- 网络拓扑纷繁复杂.而从始节点A到终节点B之间可能要经过N多的交换机.路由器.防火墙等等硬件设备,每个硬件设备的相关设定也不统一,再加上网络中可能出现的拥塞.延迟等,使得我们在编程时,处理掉线也非常棘手. 一.从程序的角度看待TCP掉线 TCP掉线的原因可能多种多样.不一而足,比如,客人的电脑突然断电.OS崩溃.路由器

核心梳理——消息处理的骨架流程——ESFramework 4.0 进阶(02)

在ESFramework 4.0 概述一文中,我们提到ESFramework.dll作为通信框架的核心,定义了消息处理的骨架流程,本文我们来详细剖析这个流程以及该骨架中所涉及的各个组件.ESFramework的骨架流程如下图所示: 一.所有的网络引擎都使用同一消息处理骨架流程 ESFramework支持TCP/UDP.二进制协议/文本协议.服务端/客户端组合而成的2x2x2=8种引擎,无论是哪一种引擎,都实现了INetEngine接口,也都使用上图所示的消息处理骨架流程来处理所接收到的所有消息.

AutoMapper 9.0快速上手,从老版本迁移到9.0+AutoMapper9.0和Autofac的完美结合

.NET模型映射器AutoMapper 9.0发布了,官方宣称不再支持静态方法调用了,老版本的部分API将在升级到9.0后,直接升级包到9.0会编译报错,所以写篇文章记录下AutoMapper新版本的学习过程吧,如果还不知道AutoMapper是什么的,建议先看这篇文章:https://masuit.com/156,或者参考官方文档:https://automapper.readthedocs.io/en/latest/Getting-started.html AutoMapper9.0快速上手

驱动力—— 通信引擎(上)—— ESFramework 4.0 进阶(03)

在ESFramework 4.0 进阶(02)-- 核心:消息处理的骨架流程一文中我们详细介绍了ESFramework中消息处理的骨架流程,并且我们已经知道,ESFramework中的所有通信引擎使用的都是这一套骨架流程.ESFramework内置了多种通信引擎以完全支持"客户端/服务端.TCP/UDP.文本协议/二进制协议"这些特性的组合.本文就来剖析ESFramework中的各种通信引擎. 一.通信引擎接口继承关系图 INetEngine是所有网络引擎的基础接口,接下来再派生出服务

如何在App中实现IM功能之二快速实现离线消息模块——箭扣科技Arrownock

如何在App中实现IM功能 之二 快速实现离线消息模块 一个App在实际使用聊天功能的时候,并非是一直呆在一个界面的,在等待好友发消息来的同时,有可能已经跳转到其他界面,也有可能切换到其他App. 在当前的App进入后台.好友发来消息时,需要给设备推送一条消息.以安卓为例,收取离线消息通知需要绑定anPush,具体做法是调用anIM.bindAnPushService(anID, AppKey, anPushType). 在App进入后台时,调用anIM.disconnect():在App回到前