.Net Remoting與WCF實現Server與Client通訊比較
.Net Remoting是微軟早前推出的一項分布式通訊技術框架,在.Net架構的程序中有著比較廣泛的應用。在WCF中,已經集成了Remoting的技術。不過,他們有著很多相同的概念,如:信道(Channel)、代理(Proxy)、寄宿(host)等。在如今仍有一些分布式系統應用中運行著由Remoting技術構建的系統。本文將描述在服務端與客戶端的交互中,他們各自的實現方式。
1、Remoting的實現。
在Remoting中,遠程對象是一個重要的概念。服務端通過將它注冊到制定的信道中,客戶端服務端公布的服務端注冊的遠程對象的URI,通過代理來使用它。在這種架構下的服務端與客戶端要實現相互之間的通訊一般是使用事件的方式進行。
Remoting的通訊模型中分三塊,即:1、客戶端、2、遠程對象、3、寄宿程序(服務端寄宿的應用程序域)。
注:遠程對象一般通過繼承自MarshalByRefObject,而繼承自MarshalByRefObject的對象是不會離開它的應用程序域的。并且為了安全,一般我們都是通過將遠程對象實現的接口提供給客戶端,而不是遠程對象。
一、客戶端發送消息到服務端。客戶端獲取來遠程對象的代理,通過使用它的提供的接口以后,通過信道的傳輸便到了遠程對象。遠程對象在收到消息后,用事件的方式通過服務端以執行相應的操作。這時,事件是定義在遠程對象中的,客戶端進行操作以后,服務端可以直接通過訂閱遠程對象的事件而獲取消息。
二、服務端發送消息到客戶端。在服務端,由于它是遠程對象注冊的應用程序域,服務端可以直接使用它。同樣,服務端發送消息給客戶端,也是通過事件來實現的。與客戶端主動發送消息給服務端不同的是,定義在遠程對象的事件運行在服務端,無法序列化到客戶端。這種情況下,一般我們可以通過一種類似"中介者"的方式來進行操作。
Remoting的程序結構如下圖:

說明:
- Client:為客戶端程序。為了可視性,為將他建成一個Winform的項目
- CommandAssembly:為公共程序集。它是客戶端與服務端共享的一個類庫,它包括遠程對象實現的接口定義。
- Host:為服務端程序,即遠程對象寄宿的應用程序。同樣它是一個Winform的項目
- RemoteObject:為遠程對象。
首先,看看遠程對象實現的接口定義:
/// <summary>
/// 服務端通知客戶端時的事件
/// </summary>
event DataChangeCallBack DataChangeEventHandler;
/// <summary>
/// 服務端促觸發事件函數
/// </summary>
/// <param name="entityName"></param>
void ServerSideDataChange(string entityName);
/// <summary>
/// 客戶端調用服務端使用的接口函數
/// </summary>
/// <param name="entityName"></param>
void ClientSideDataChange(string entityName);
其次:遠程對象的定義:
public class RemoteObject : MarshalByRefObject, IDataChange
{
public event DataChangeCallBack DataChangeEventHandler;
public event DataChangeCallBack ClientDataChangeEventHandler;
#region IDataChange 成員
public void ServerSideDataChange(string entityName)
{
if (DataChangeEventHandler == null) return;
foreach (Delegate datachange in DataChangeEventHandler.GetInvocationList())
{
DataChangeCallBack temp = datachange as DataChangeCallBack;
try
{
temp(entityName);
}
catch
{
DataChangeEventHandler -= temp;
}
}
}
public void ClientSideDataChange(string entityName)
{
if (ClientDataChangeEventHandler != null)
{
ClientDataChangeEventHandler(entityName);
}
}
#endregion
public override object InitializeLifetimeService()
{
return null;
}
}
注意:在遠程對象的顯示中,重寫來基類的InitializeLifetimeService函數,目的是為了遠程對象永不過期。這涉及到遠程對象生命周期的問題。有興趣的同學可以了解一下(為記憶中,遠程對象生存期為6分鐘)。如果不重寫此函數,在遠程對象被GC回收后,如果再有客戶端想通過URI獲取它的代理將會出錯。因為它早已被GC回收了。
再次,主要看看服務端通知客戶端時,使用的"中介者"對象的定義:
public class EventWrapper : MarshalByRefObject,IDataChange
{
public event DataChangeCallBack DataChangeEventHandler;
#region IDataChange 成員
public void ServerSideDataChange(string entityName)
{
if (DataChangeEventHandler!=null)
{
DataChangeEventHandler(entityName);
}
}
public void ClientSideDataChange(string entityName)
{
}
#endregion
}
最后就是服務端與客戶端信道、事件注冊,以及消息的發送問題了。這里主要給出客戶端實現(因為涉及服務端事件通知客戶端的問題)。服務端類似。
private void ClientForm_Load(object sender, EventArgs e)
{
UnregisterChannels();
RegisterChannel();
RegisterEvent();
}
void UnregisterChannels()
{
if (ChannelServices.RegisteredChannels.Length == 0)
{
return;
}
foreach (IChannel channel in ChannelServices.RegisteredChannels)
{
ChannelServices.UnregisterChannel(channel);
}
}
void RegisterChannel()
{
IDictionary hashtable = new Hashtable();
hashtable["port"] = 0;
BinaryClientFormatterSinkProvider provider = new BinaryClientFormatterSinkProvider();
TcpChannel tcpChannel = new TcpChannel(hashtable, provider, null);
ChannelServices.RegisterChannel(tcpChannel, true);
}
void RegisterEvent()
{
instance = Activator.GetObject(typeof(IDataChange), "tcp://127.0.0.1:8888/DataService") as IDataChange;
wrapper = new EventWrapper();
wrapper.DataChangeEventHandler+=new DataChangeCallBack(wrapper_DataChangeEventHandler);
instance.DataChangeEventHandler += wrapper.ServerSideDataChange;
}
void wrapper_DataChangeEventHandler(string entityName)
{
if (txtReceiveMsg.InvokeRequired)
{
txtReceiveMsg.Invoke(new ShowMessageCallBack(wrapper_DataChangeEventHandler), entityName);
}
else
{
txtReceiveMsg.Text = entityName;
}
}
private void btnSend_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtSendMsg.Text))
{
instance.ClientSideDataChange(txtSendMsg.Text);
}
}
程序運行界面如下圖:
服務端:

客戶端:

注:在信道的定義中,應該使用與之對應的格式化標識符。
2、WCF的實現
在WCF中,實現服務端與客戶端的通訊一般通過雙工的模式進行。在WCF中支持雙工的綁定協議有:netTcpBinding、wsDualHttpBingding。在WCF實現的服務端與客戶端通訊的示例中,為選擇來前一種協議。
程序的結構圖如下:

同樣,為了可視化,Host與Client也為Winform程序。
在WCF的實現中,實現的功能為Remoting完全相同。
首先看看服務契約定義:
[ServiceContract(CallbackContract=typeof(ICallBack))]
public interface IDataChange
{
[OperationContract(IsOneWay=true)]
void Register();
[OperationContract(IsOneWay=true)]
void ServerSideDataChange(string entityName);
[OperationContract(IsOneWay = true)]
void ClientSideDataChange(string entityName);
}
回調契約定義:
public interface ICallBack
{
[OperationContract(IsOneWay=true)]
void DataChange(string entityName);
}
服務的實現代碼:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Single)]
public class DataService : IDataChange
{
public event DataChangeCallBack ReceiveClientDataChangeHandler;
ICallBack callBack;
#region IDataChange 成員
public void Register()
{
callBack = OperationContext.Current.GetCallbackChannel<ICallBack>();
}
public void ServerSideDataChange(string entityName)
{
callBack.DataChange(entityName);
}
public void ClientSideDataChange(string entityName)
{
if (ReceiveClientDataChangeHandler != null)
{
ReceiveClientDataChangeHandler(entityName);
}
}
#endregion
}
服務在Winform中的寄宿代碼如下:
private void ServerFrom_Shown(object sender, EventArgs e)
{
Thread thread = new Thread(ServiceStart);
thread.Start();
}
void ServiceStart()
{
Uri address = new Uri("net.tcp://127.0.0.1:8806/DataService");
using (ServiceHost host = new ServiceHost(service, address))
{
host.Open();
tipStatus.Text = "服務已經啟動";
while (true)
{
Thread.Sleep(100);
}
}
}
而在客戶端,直接通過窗體實現了回調接口,將窗體封裝在InstanceContext里,讓窗體對象成為服務端回調操作的對象,服務端回調客戶端時直接將信息發送給窗體。在客戶端窗體的FormLoad事件里做如下定義:
InstanceContext context = new InstanceContext(this);
dataChange = new ClientProxy(context);
dataChange.Register();
客戶端通知服務端的方式比較簡單,也就是主動去調用服務接口。代碼如下:
if (!string.IsNullOrEmpty(txtSendMsg.Text))
{
dataChange.ClientSideDataChange(txtSendMsg.Text);
}
這樣,在客戶端調用服務端接口時候,觸發服務端事件來通知窗體做數據顯示。
服務通知客戶端時通過回調客戶端,將消息發送給客戶端。代碼如下:
Form.cs
if (!string.IsNullOrEmpty(txtSendMsg.Text))
{
service.ServerSideDataChange(txtSendMsg.Text);
}
//DataSerive.cs
public void ServerSideDataChange(string entityName)
{
callBack.DataChange(entityName);
}
如此這般,便實現了在WCF架構下實現服務端與客戶端的通訊。
運行界面與上面類似。就不重復給出了。
浙公網安備 33010602011771號