基於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
    }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM