最近在项目中,遇到了一个需要在远程监视自动运行软件的实时情况的例子。因为MS面向服务方面有WCF,而且看了一些资料,觉得WCF比较适合这个应用。因此决定用WCF来实现这个功能。

首先,先说一下具体的应用,监控,顾名思义,有两个方面的意思,一方面是”监”,也就是远程要能实时查看终端的各种情况。这里其实指的就是被监控的要能主动的,实时的向远程控制端发送自己的情况。另一方面是控,即远程端能够发布命令控制终端进行执行。并由终端返回一定的执行信息。

而且这里是一种一对一对多的关系,即一个终端可以被多个管理端监控。实际上一个管理端也可以监控多个终端,在是这种分析中,我们可以明白,真正运行服务的是终端,而不是管理端。

简单起见,假定远程发送命令的操作是void Operation(),终端更新的操作是UpdateStatus();

这个想法,从设计到实现,经历以下三种阶段的变形。

最初的想法,是一个Service类。一个接口。即

即控制端调用operation发送命令,终端调用UpdateStatus更新状态。

对应的,要有一定的回调,即控制端发送Operation时,终端要有一个callBack进行接收,相应的,当终端用UpdateStatus时,控制端也要有一个callBack进行接收。

当这两种操作被集成到一个服务中时,我们的wcf接口变成了如下结构。


Code 
  
namespace Geyunfei.WCF
 {
     [ServiceContract(

     SessionMode = SessionMode.Required
    ,
   CallbackContract = typeof(ISeviceCallBack)
          )]
     public interface IService
     {
         [OperationContract]
         /// 
         /// 远程发送控制命令
         /// 
         /// 
        void Operation();

         [OperationContract]
         /// 
         /// 更新状态
         /// 
        void UpdateStatus();
     }
     
     public interface ISeviceCallBack
     {

       
         void ReceiveStatus();
         
         void ReceiveCommand();
     }
     public static class Global
     {
         public static List<ISeviceCallBack> callBacks = new List<ISeviceCallBack>();
     }
     [ServiceBehavior]
     public class Service : IService,IDisposable 
     {
         ISeviceCallBack callBack;
         #region IService Members
         public Service()
         {
             callBack = System.ServiceModel.OperationContext.Current.GetCallbackChannel<ISeviceCallBack>();

             Global.callBacks.Add(callBack);
            
         }

         /// 
         /// 当服务端调用这个时,向终端发命令
         /// 
        public void Operation()
         {

             foreach (var r in Global.callBacks)
             {
                 r.ReceiveCommand();
             }
         }
         /// 
         /// 当终端调用时,向服务端发命令
         /// 
        public void UpdateStatus()
         {
             foreach (var r in Global.callBacks)
             {
                 r.ReceiveStatus();
             }
         }

         #endregion

         #region IDisposable Members

         public void Dispose()
         {
             Global.callBacks.Remove(this.callBack);
         }

         #endregion
     }
 }


 

这样做实现起来比较方便,但是缺点也是很明显的,因为实际上终端只需要调用updateStatus,并回调receiveCommand,而管理端只需要调用Operation,回调receiveUpdateStatus(),现在这两种操作同时暴露给了终端和管理端 ,因此从设计上,这是一种不安全设计。。而且在调用相应的操作时,服务端自己又回得到相应的callBack,让人感到很费解。

那么下一步的想法,显然是把面向终端和管理端的服务进行分开。同时,用全局的变量或MSMQ进行交互,这里为了简单起见,只使用了List<>,没有使用委托。

这时,我们的设计变成了如下的形式:


Code 
  

namespace Geyunfei.WCF2
 {
     /// 
     /// 面向终端的服务
     /// 
    [ServiceContract(

     SessionMode = SessionMode.Required
    ,
   CallbackContract = typeof(ITerminalSeviceCallBack )
          )]
     public interface ITerminalService
     {
         [OperationContract]
         /// 
         /// 更新状态
         /// 
        void UpdateStatus();
     }

     /// 
     /// 面向管理端的服务
     /// 
    [ServiceContract(

     SessionMode = SessionMode.Required
    ,
   CallbackContract = typeof(IControlSeviceCallBack)
          )]
     public interface IControlService
     {
         [OperationContract]
         /// 
         /// 远程发送控制命令
         /// 
         /// 
        void Operation();
     }

     public interface ITerminalSeviceCallBack
     {
         void ReceiveCommand();
     }

     public interface IControlSeviceCallBack
     {
         void ReceiveStatus();
     }
     public static class Global
     {
         public static List<IControlSeviceCallBack > controlcallBacks = new List<IControlSeviceCallBack >();
         public static List<ITerminalSeviceCallBack> terminalcallBacks = new List<ITerminalSeviceCallBack>();
     }
     [ServiceBehavior]
     public class TerminalService : ITerminalService, IDisposable
     {
          ITerminalSeviceCallBack callBack;
         #region IService Members
          public TerminalService()
         {
             callBack = System.ServiceModel.OperationContext.Current.GetCallbackChannel<ITerminalSeviceCallBack>();
             Global.terminalcallBacks .Add(callBack);

         }
         /// 
         /// 当终端调用时,向服务端发命令
         /// 
        public void UpdateStatus()
         {
             foreach (var r in Global.controlcallBacks)
             {
                 r.ReceiveStatus();
             }
         }

         #endregion

         #region IDisposable Members

         public void Dispose()
         {
             Global.terminalcallBacks .Remove(this.callBack);
         }

         #endregion
     }


     [ServiceBehavior]
     public class ControlService : IControlService, IDisposable
     {
         IControlSeviceCallBack  callBack;
         #region IService Members
         public ControlService()
         {
             callBack = System.ServiceModel.OperationContext.Current.GetCallbackChannel<IControlSeviceCallBack >();

             Global.controlcallBacks .Add(callBack);

         }

         /// 
         /// 当服务端调用这个时,向终端发命令
         /// 
        public void Operation()
         {

             foreach (var r in Global.terminalcallBacks)
             {
                 r.ReceiveCommand();
             }
         }

         #endregion

         #region IDisposable Members

         public void Dispose()
         {
             Global.controlcallBacks .Remove(this.callBack);
         }

         #endregion
     }
 }



现在,终端和管理端的服务分开了,接口也清晰了。

但是这样做又有另一个缺点,即我的每一个终端是只运行一个软件的,即我在运行terminalService时,只有一个点在接入。这时,我为这一个点开一个服务,是一种浪费 。

于是,就有了第三种方案。即服务是面向管理端的。终端只实例一个简单的承载体,如下:

这时,我们的方案就已经接近完美了。


Code 
  

namespace Geyunfei.WCF3
 {
     [ServiceContract(

     SessionMode = SessionMode.Required
    ,
   CallbackContract = typeof(ISeviceCallBack)
          )]
     public interface IService
     {
         [OperationContract]
         /// 
         /// 远程发送控制命令
         /// 
        void Operation();
     }

     public interface ISeviceCallBack
     {
         void ReceiveStatus();
     }
     public static class Global
     {
         public static List<ISeviceCallBack> callBacks = new List<ISeviceCallBack>();
         public static ReceiveCommandHandler receiveCommad;
     }
     [ServiceBehavior]
     public class Service : IService, IDisposable
     {
         ISeviceCallBack callBack;
         #region IService Members
         public Service()
         {
             callBack = System.ServiceModel.OperationContext.Current.GetCallbackChannel<ISeviceCallBack>();
             Global.callBacks.Add(callBack);

         }
         /// 
         /// 当终端调用时,向服务端发命令
         /// 
        public void Operation()
         {

             if (Global.receiveCommad != null)
                 Global.receiveCommad();
         }
         #endregion
         #region IDisposable Members
         public void Dispose()
         {
             Global.callBacks.Remove(this.callBack);
         }

         #endregion
     }


    
     public delegate void ReceiveCommandHandler();
     public class ServiceHost
     {
         public ServiceHost()
         {
         }

         public void Start()
         {
             //在这里启动运行一个Service服务
        }
         public void Stop()
         {
             //在这里停止服务
        }


         /// 
         /// 当收到远程的命令时,触发此事件
         /// 
        public event ReceiveCommandHandler ReceveCommand
         {
             add
             {
                 Global.receiveCommad += value;
             }
             remove
             {
                 Global.receiveCommad -= value;
             }
         }


         /// 
         /// 更新状态
         /// 
        public void UpdateStatus()
         {
             foreach (var r in Global.callBacks)
             {
                 r.ReceiveStatus();
             }
         }

     }
 }



以上是我在开发中的一些体会,从第一种到第三种,可能老鸟们看着很弱智,只接就能使用第三种设计,但是对于一个初学者,直接想到这种方案可能比较困难,欢迎大家讨论。