[WCF 學習筆記] 3. 消息交換

客戶端和服務之間通過消息交換(Message Exchange)來完成方法調用和數據傳遞,WCF 定義了 3 種消息交換模式。

1. Request/Reply

這是缺省模式,又被稱之爲同步調用。在調用服務方法後需要等待服務的消息返回,即便該方法返回 void 類型。

[ServiceContract]
public interface IContract
{
  [OperationContract]
  void Test();
}

public class MyService : IContract
{
  public void Test()
  {
    Thread.Sleep(3000);
    Console.WriteLine("Test Execute:{0}", DateTime.Now);
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(MyService));
      host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),
        "http://localhost:8080/myservice");

      host.Open();
    });

    ChannelFactory<IContract> factory = new ChannelFactory<IContract>(new BasicHttpBinding(),
      "http://localhost:8080/myservice");
    IContract o = factory.CreateChannel();

    Console.WriteLine("Start:{0}", DateTime.Now);
    o.Test();
    Console.WriteLine("End:{0}", DateTime.Now);
  }
}

輸出:
Start:2007-3-27 15:26:07
Test Execute:2007-3-27 15:26:10
End:2007-3-27 15:26:10

2. one-way

這種方式在調用方法後會立即返回,非常類似於異步行爲。不過需要注意的是 one-way 不能用在非void,或者包含 out/ref 參數的方法上,會導致拋出 InvalidOperationException 異常。
[ServiceContract]
public interface IContract
{
  [OperationContract(IsOneWay=true)]
  void Test();
}

public class MyService : IContract
{
  public void Test()
  {
    Thread.Sleep(3000);
    Console.WriteLine("Test Execute:{0}", DateTime.Now);
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(MyService));
      host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),
        "http://localhost:8080/myservice");    
      host.Open();
    });

    ChannelFactory<IContract> factory = new ChannelFactory<IContract>(
      new BasicHttpBinding(), "http://localhost:8080/myservice");
    IContract o = factory.CreateChannel();

    Console.WriteLine("Start:{0}", DateTime.Now);
    o.Test();
    Console.WriteLine("End:{0}", DateTime.Now);
  }
}

輸出:
Start:2007-3-27 15:27:18
End:2007-3-27 15:27:18
Test Execute:2007-3-27 15:27:21

3. duplex

這種模式相對複雜一些,我們詳細描述一下步驟:

(1) 設計標準的服務契約。爲了完成回調操作,我們必須指定 SessionMode 和回調類型。
[ServiceContract(SessionMode=SessionMode.Required, CallbackContract=typeof(ICallback))]
public interface IContract
{
  [OperationContract]
  void Test();
}

(2) 設計回調接口類型。由於回調方法在客戶端執行,因此無須添加 ServiceContractAttribute。對於回調操作,服務器無須獲取其返回信息,因此添加 IsOneWay=true 特性參數。
public interface ICallback
{
  [OperationContract(IsOneWay=true)]
  void Call(DateTime d);
}

(3) 實現服務契約。通過 OperationContext.Current.GetCallbackChannel 可以獲取回調委託,進而完成調用。
public class MyService : IContract
{
  ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();

  public void Test()
  {
    Console.WriteLine("Test AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
    callback.Call(DateTime.Now);
  }
}

(4) 實現服務器。注意必須使用支持 Session 的 Binding 類型。
ServiceHost host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IContract), new WSDualHttpBinding(), "http://localhost:8080/myservice");
host.Open();

(5) 創建客戶端代理對象。利用 Svcutil 或者我們前面講的方法創建客戶端代理類型代碼。(爲顯示方便,代碼有所刪減。)
//------------------------------------------------------------------------------
// <auto-generated>
//   此代碼由工具生成。
//   運行庫版本:2.0.50727.42
//
//   對此文件的更改可能會導致不正確的行爲,並且如果
//   重新生成代碼,這些更改將會丟失。
// </auto-generated>
//------------------------------------------------------------------------------

[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute(ConfigurationName = "IContract", CallbackContract = typeof(IContractCallback), SessionMode = SessionMode.Required)]
public interface IContract
{
  [OperationContractAttribute(Action = "...", ReplyAction = "...")]
  void Test();
}

[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IContractCallback
{
  [OperationContractAttribute(IsOneWay = true, Action = "...")]
  void Call(System.DateTime d);
}

[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IContractChannel : IContract, IClientChannel
{
}

[DebuggerStepThroughAttribute()]
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class ContractClient : DuplexClientBase<IContract>, IContract
{
  public void Test()
  {
    base.Channel.Test();
  }
}

(6) 實現客戶端。

創建新的客戶端項目,將生成的代理文件加入。客戶端除了要創建一個實現回調接口的類型外,還要爲綁定對象指定一個監聽端口,以便服務器與之聯繫。
class CallBack : IContractCallback
{
  public void Call(DateTime d)
  {
    Console.WriteLine("Call AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
    Console.WriteLine("Server DateTime:{0}", d);
  }
}

[STAThread]
static void Main(string[] args)
{
  WSDualHttpBinding binding = new WSDualHttpBinding();
  binding.ClientBaseAddress = new Uri("http://localhost:8081/client");
  EndpointAddress address = new EndpointAddress("http://localhost:8080/myservice");

  ContractClient client = new ContractClient(new InstanceContext(new CallBack()), binding, address);
  client.Test();
}

輸出:

Server
--------------
Test AppDomain:Server

Client
--------------
Call AppDomain:Client.vshost.exe
Server DateTime:2007-3-27 17:01:39
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章