最近在項目中,遇到了一個需要在遠程監視自動運行軟件的實時情況的例子。因為MS面向服務方面有 WCF,而且看了一些資料,覺得WCF比較適合這個應用。因此決定用WCF來實現這個功能。
首先,先說一下具體的應用,監控,顧名思義,有兩個方面的意思,一方面是”監”,也就是遠程要 能實時查看終端的各種情況。這裡其實指的就是被監控的要能主動的,實時的向遠程控制端發送自己的情 況。另一方面是控,即遠程端能夠發布命令控制終端進行執行。並由終端返回一定的執行信息。
而且這裡是一種一對一對多的關系,即一個終端可以被多個管理端監控。實際上一個管理端也可以監 控多個終端,在是這種分析中,我們可以明白,真正運行服務的是終端,而不是管理端。
簡單起見,假定遠程發送命令的操作是void Operation(),終端更新的操作是UpdateStatus();
這個想法,從設計到實現,經歷以下三種階段的變形。
最初的想法,是一個Service類。一個接口。即
即控制端調用operation發送命令,終端調用UpdateStatus更新狀態。
對應的,要有一定的回調,即控制端發送Operation時,終端要有一個callBack進行接收,相應的,當 終端用UpdateStatus時,控制端也要有一個callBack進行接收。
當這兩種操作被集成到一個服務中時,我們的wcf接口變成了如下結構。
namespace Geyunfei.WCF
{
[ServiceContract(
SessionMode = SessionMode.Required
,
CallbackContract = typeof(ISeviceCallBack)
)]
public interface IService
{
[OperationContract]
/// <summary>
/// 遠程發送控制命令
/// </summary>
///
void Operation();
[OperationContract]
/// <summary>
/// 更新狀態
/// </summary>
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);
}
/// <summary>
/// 當服務端調用這個時,向終端發命令
/// </summary>
public void Operation()
{
foreach (var r in Global.callBacks)
{
r.ReceiveCommand();
}
}
/// <summary>
/// 當終端調用時,向服務端發命令
/// </summary>
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<>,沒有使用委托。
這時,我們的設計變成了如下的形式:
namespace Geyunfei.WCF2
{
/// <summary>
/// 面向終端的服務
/// </summary>
[ServiceContract(
SessionMode = SessionMode.Required
,
CallbackContract = typeof(ITerminalSeviceCallBack )
)]
public interface ITerminalService
{
[OperationContract]
/// <summary>
/// 更新狀態
/// </summary>
void UpdateStatus();
}
/// <summary>
/// 面向管理端的服務
/// </summary>
[ServiceContract(
SessionMode = SessionMode.Required
,
CallbackContract = typeof(IControlSeviceCallBack)
)]
public interface IControlService
{
[OperationContract]
/// <summary>
/// 遠程發送控制命令
/// </summary>
///
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&g t;();
Global.terminalcallBacks .Add(callBack);
}
/// <summary>
/// 當終端調用時,向服務端發命令
/// </summary>
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);
}
/// <summary>
/// 當服務端調用這個時,向終端發命令
/// </summary>
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時 ,只有一個點在接入。這時,我為這一個點開一個服務,是一種浪費 。
於是,就有了第三種方案。即服務是面向管理端的。終端只實例一個簡單的承載體,如下:
這時,我們的方案就已經接近完美了。
namespace Geyunfei.WCF3
{
[ServiceContract(
SessionMode = SessionMode.Required
,
CallbackContract = typeof(ISeviceCallBack)
)]
public interface IService
{
[OperationContract]
/// <summary>
/// 遠程發送控制命令
/// </summary>
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);
}
/// <summary>
/// 當終端調用時,向服務端發命令
/// </summary>
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()
{
//在這裡停止服務
}
/// <summary>
/// 當收到遠程的命令時,觸發此事件
/// </summary>
public event ReceiveCommandHandler ReceveCommand
{
add
{
Global.receiveCommad += value;
}
remove
{
Global.receiveCommad -= value;
}
}
/// <summary>
/// 更新狀態
/// </summary>
public void UpdateStatus()
{
foreach (var r in Global.callBacks)
{
r.ReceiveStatus();
}
}
}
}
以上是我在開發中的一些體會,從第一種到第三種,可能老鳥們看著很弱智,只接就能使用第三種設 計,但是對於一個初學者,直接想到這種方案可能比較困難,歡迎大家討論。