基于WCF回调(WCF Callback)的GPS报警推送
基于WCF回调(WCF Callback)的GPS报警推送
报警推送数据在很多软件中都有需求,比如任务提醒、消息广播、实时的监控报警等等。凡是对实时性要求越高的场景,越是需要服务器及时、准确地向客户端推送数据。一般的推送,我们可以选择使用socket,因为socket是双工通信的最佳模式。但是直接使用socket来开发,对于复杂的报警逻辑、权限判断、报警注册、数据库调用和更新处理来说,使用Socket处理,代码比较难以维护。
考虑到目前的基于部标808的GPS平台(需要完整平台源码的可以联系我购买),我们决定使用WCF来作为平台的基础服务架构,而WCF的回调模式可以满足GPS报警复杂的业务模式:
WCF推送报警 已下载 1800 次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.<span style= "color: #ff0000;" ><strong>IGpsEventServiceCallback</strong></span> { 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 } |
(10413)