• 基于WCF回调(WCF Callback)的GPS报警推送(带源码)


    基于WCF回调(WCF Callback)的GPS报警推送

    报警推送数据在很多软件中都有需求,比如任务提醒、消息广播、实时的监控报警等等。凡是对实时性要求越高的场景,越是需要服务器及时、准确地向客户端推送数据。一般的推送,我们可以选择使用socket,因为socket是双工通信的最佳模式。但是直接使用socket来开发,对于复杂的报警逻辑、权限判断、报警注册、数据库调用和更新处理来说,使用Socket处理,代码比较难以维护。

    考虑到目前的基于部标808的GPS平台(需要完整平台源码的可以联系我购买),我们决定使用WCF来作为平台的基础服务架构,而WCF的回调模式可以满足GPS报警复杂的业务模式:

    GpsAlarmService 源码下载

    1.注册
    用户注册后,需要加载自己分配的功能权限和数据权限,功能权限决定了是否能看报警。
    数据权限,决定了能看到那些报警,那些车辆的报警。

    2.GPS报警发布
    通常我们将808GPS服务器作为报警发布者,当接收到车辆GPS终端发送上来的报警后,发布给报警服务模块,由报警服务模块再根据逻辑转发给订阅者.

    3.报警订阅
    部标808协议规定了32路的报警再加上其他扩展的平台报警,可多达几十种报警,客户端需要通过订阅功能来接收自己感兴趣的报警。

    4.报警过滤
    报警最大的问题,不是如何实时的推送到客户端,而是如何避免误报。需要有一套算法设定来过滤掉无效的报警。频繁的误报,会对客户造成困扰,也会造成狼来了的效果,多次误报后,用户就失去了对报警的信任。如在工厂围墙的红外监控报警,报警设定的过于敏感,一有风吹草动就报警,保安就不得休息,时间长了就不看它,当有人非法翻越围墙的时候,反而没有看到。简单的过滤,就是时间过滤法,如当报警超过10秒后推送到客户端。

    5.报警显示与处理
    报警如何显示,如何避免重复显示,累积的未处理报警如何处理等等,这个也是个比较麻烦的用户体验的问题,很少有人去问问用户是否反感不断弹屏的功能设计。

    基于WCF回调的双工通信,可以很好的完成报警推送。WCF中NetTcpBinding支持回调,因为从本质上讲TCP和IPC协议支持双向通信.

    实现步骤:

    1)首先定义报警服务接口(契约),提供订阅、注销、发布的外部接口功能。

    namespace GpsNET
    {
        /**
         * Gps报警推送服务
         * Author: http://cnblogs.com/productivity     * 
         */
        [ServiceContract(SessionMode=SessionMode.Required,
            CallbackContract=typeof(IGpsServiceCallback))]
        interface IGpsEventService
        {
            /**
             * 订阅
             * UserId 注册用户ID
             * Alarms 要订阅的报警类型ID
             * 注意IsOneWay = true,避免回调时发生死锁
             */
            [OperationContract(IsOneWay = true)]
            void Subscribe(int UserId, List<int> Alarms);
    
            //注销
            [OperationContract(IsOneWay = true)]
            void Unsubscribe(int UserId);
        }
    }
    

      

    2)定义GPS事件回调函数,当发生报警时,客户端会自动触发事件,关于IsOneWay = true这里就不多说了。

    namespace GpsNET
    {
        /**
         * 报警回调
         */
        public interface IGpsServiceCallback
        {
            /**
             * msgItems 接收到的报警事件集合
             */
            [OperationContract(IsOneWay = true)]
            void OnMessageReceived(List<AlarmItem> msgItems);
        }
    }
    

      

    3)报警服务实现

    namespace GpsNET
    {
        /**
         * Gps报警推送服务
         * Author: http://cnblogs.com/productivity     * 
         */
        [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
        internal sealed class GpsEventService:IGpsEventService
        {
            protected static log4net.ILog logger = log4net.LogManager.GetLogger(typeof(GpsEventService));
            public delegate void CallbackDelegate<T>(T t);
            //客户端的报警消息接收事件
            public static CallbackDelegate<List<AlarmItem>> MessageReceived;
            //订阅者
            public static List<AlarmSubscriber> Subscribers = new List<AlarmSubscriber>();
    
            //用户订阅报警,Alarms代表要订阅的报警类型
            public void Subscribe(int UserId, List<int> Alarms)
            {
                IGpsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IGpsServiceCallback>();
                
                User u = GetUser(UserId);
                AlarmSubscriber subscriber =  GetSubscirber(UserId);
                if (subscriber == null)
                {
                    subscriber = new AlarmSubscriber();
                    subscriber.User = u;
                    Subscribers.Add(subscriber);
                    logger.Info("客户端" + UserId + "注册");
                }
                subscriber.Alarms = Alarms; //更新订阅
                subscriber.ClientCallback = callback;
                //绑定退出事件,在客户端退出时,注销客户端的订阅
                ICommunicationObject obj = (ICommunicationObject)callback;
                obj.Closed += new EventHandler(GpsEventService_Closed);
                obj.Closing += new EventHandler(GpsEventService_Closing);        
            }
    
            private AlarmSubscriber GetSubscirber(int UserId)
            {
                foreach(AlarmSubscriber sub in Subscribers)
                {
                    if (sub.User.Id == UserId)
                        return sub;
                }
                return null;
            }
    
            private User GetUser(int UserId)
            {
                return new User(UserId);
            }
    
            void GpsEventService_Closing(object sender, EventArgs e)
            {
                logger.Info("客户端关闭退出...");
            }
    
            void GpsEventService_Closed(object sender, EventArgs e)
            {
                IGpsServiceCallback callback = (IGpsServiceCallback)sender;
                Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
                {
                    if (subscriber.ClientCallback == callback)
                    {
                        Subscribers.Remove(subscriber);
                        logger.Info("用户" + subscriber.User.Id + "Closed Client Removed!");
                    }
    
                });
                
            }
            //客户端断开
            public void Unsubscribe(int UserId)
            {
                IGpsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IGpsServiceCallback>();
                
                Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
                {
                    if (subscriber.User.Id == UserId)
                    {
                        Subscribers.Remove(subscriber);
                        logger.Info("用户" + subscriber.User.Id + "注销 Client Removed!");
                    }
    
                });
            }
    
            //向客户端推送报警数据
            public static void SendAlarmMessage(List<AlarmItem> alarmItems)
            {
                //没有要推送的报警数据
                if (alarmItems.Count == 0)
                    return;
    
                Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
                {
                    ICommunicationObject callback = (ICommunicationObject)subscriber.ClientCallback;
                    if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                    {
                        try
                        {
                            //此处需要加上权限判断、订阅判断等
                            subscriber.ClientCallback.OnMessageReceived(alarmItems);
                        }
                        catch (Exception ex)
                        {
                            Subscribers.Remove(subscriber);
                            logger.Error("用户" + subscriber.User.Id + "出错:" + ex.Message);
                            logger.Error(ex.StackTrace);
                        }
                    }
                    else
                    {
                        Subscribers.Remove(subscriber);
                        logger.Info("用户" + subscriber.User.Id + "Closed Client Removed!");
                    }
                });
            }
           
            //通知用户服务已经停止
            public static void NotifyServiceStop()
            {
                List<AlarmItem> msgItems = new List<AlarmItem>();
                msgItems.Add(new AlarmItem(0,"Stop"));
                SendAlarmMessage(msgItems);
            }
        }
    }
    

      4)客户端调用

     public partial class Form1 : Form, GpsAlarm.IGpsEventServiceCallback
        {
            int UserId = 1;
            public Form1()
            {
                InitializeComponent();
            }
    
            GpsAlarm.GpsEventServiceClient client;
            private void Form1_Load(object sender, EventArgs e)
            {
                try
                {
                    client = new GpsAlarm.GpsEventServiceClient(new InstanceContext(this));//注意Form要实现接口
                    //注册并订阅报警类型是1,2,3
                    client.Subscribe(UserId, new int[]{1,2,3});
                    listBox1.Items.Add("注册成功,等待消息推送");
                }
                catch (Exception ex)
                {
                    listBox1.Items.Add(ex.ToString());
                }
            }
    
            #region IEventSystemCallback Members
            /**
             * 监听报警事件
             */
            public void OnMessageReceived(AlarmItem[] msgItems)
            {
                foreach (AlarmItem mi in msgItems)
                {
                    listBox1.Items.Add(mi.Name);
                }
            }
            #endregion
        }
    

      

    我的微信号: jt808_com 添加微信
  • 相关阅读:
    node异步转同步(循环)
    三级省市区PCASClass.js插件
    微信公众号基础总结(待更新)
    ES6详解
    webpack配置
    高性能 CSS3 动画
    github上传口令
    纯css3 实现3D轮播图
    优美的js代码,拿去玩~
    关于列举属性用点还是用【】
  • 原文地址:https://www.cnblogs.com/productivity/p/3094818.html
Copyright © 2020-2023  润新知