程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF從理論到實踐(7):消息交換模式

WCF從理論到實踐(7):消息交換模式

編輯:關於.NET

本文的出發點

通過閱讀本文,您能理解以下知識:

WCF定義了哪幾種消息交換模式?

One-Way Calls

Request/Reply

Duplex

用示例來解析WCF的消息交換模式

本文適合的讀者

本文涉及到了SOA中的消息交換的基礎概念,需要一些初級的Xml Web Service和分布式系統開發的經驗,最好理解WCF架構

WCF定義了哪幾種消息交換模式?

WCF定義了三種消息交換方式 ,分別為:

One-Way Calls

Request/Reply

Duplex

One-Way Calls

在幾種消息交換模式中,one-way calls是最沒良心的,對於客戶端,one-way calls就如肉包子打狗,有去無回。下面的圖示給出這種交換模型的特征:

在這種交換模式中,存在著如下的特征

沒有返回值,返回類型只能為void

不能包含ref或者out類型的參數

只有客戶端發起請求,服務端並不會對請求進行回復。

通過設置OperationContract的IsOneWay=True可以將滿足要求的方法設置為這種消息交換模式,方法如下:

[OperationContract(IsOneWay=true)]

void Test(int intVal);

上面的代碼,就是將方法Test設置成為了one-way call的消息交換模式,注意如果Test方法的返回類型不是void或者帶有ref或者out類型的參數,都會拋出異常InvalidOperationException,如下面列表中的方法均不能被聲明為one-way模式

int Test(int intVal);

int Test();

int Test();

void Test(ref int intVal);

void Test(out int intVal);

Request/Reply

request/reply比起one-way來說,就更懂得禮尚往來,它是缺省的消息交換模式,類似於http協議中的請求/響應模型。下面的圖示給出這種交換模式的特征:

這種交換模式是使用最多的一中,它有如下特征:

調用服務方法後需要等待服務的消息返回,即便該方法返回 void 類型

相比Duplex來講,這種模式強調的是客戶端的被動接受,也就是說客戶端接受到響應後,消息交換就結束了。

在這種模式下,服務端永遠是服務端,客戶端就是客戶端,職責分明。

它是缺省的消息交換模式,設置OperationContract便可以設置為此種消息交換模式

[OperationContrac]

void Test(int intVal);

注意,盡管Test方法返回為void,但Server也會生成reply響應並發送給client.有來有往是這種模式的特征。

Duplex

這種交換模式比起上面兩種,比較復雜,它和request/reply模式類似,也是有來有往,但處理過程卻比request/reply要復雜,因為它可以在處理完請求之後,通過請求客戶端中的回調進行響應操作,這種模式的圖示為:

注意,這種方式和request/reply方式的圖示也很類似,當二者存在著至關重要的不同,它在客戶端也有監聽節點,在callback的時候,服務器和客戶端的角色會進行交換,服務端此時成了嚴格意義上的客戶端,而客戶端此時能接受服務端的callback請求,所以成為了服務端。呵呵,辯證法,都拗口死了,當事實就是這種,就像對與錯一樣,會相互轉換,失敗是成功之母,而成功是失敗之源。廢話少說,Duplex的特征主要包括

消息交換過程中,服務端和客戶端角色會發生調換

服務端處理完請求後,返回給客戶端的不是reply,而是callback請求。

打個比方,Reqeust/Reply方式像是搓澡,1個管搓,1個被搓

而duplex像是拳擊,兩個人都會出拳

Duplex模式對Bindding有特殊的要求,它要求支持Duplex MEP(Message Exchange Pattern),如WSDualHttpBinding和NetTcpBinding,有關Binding的介紹請參見http://www.cnblogs.com/jillzhang/archive/2008/02/03/1063406.html

用示例來解析WCF的消息交換模式

建立示例的步驟不做具體闡述,下面看一下項目的最終結構:

下表說明各個項目的作用

項目名稱 項目作用 包含文件 Jillzhang.Messaging.Contract 定義WCF服務端和客戶端共同使用的Contract接口 IOneWayJob.cs

 

INormalJob.cs

IJob.cs

ICallback.cs

Jillzhang.Messaging.Service 實現WCF服務的Contract OneWayJob.cs

 

NormalJob.cs

Job.cs

Jillzhang.Messaging.Host 一個Console應用程序,用於承載WCF服務端 Program.cs

 

App.config

Jillzhang.Messaging.WebSite 一個用於WebSite,用於承載WCF服務。是例外一中Host OnewayService.svc

 

NormalJobService.svc

JobService.svc

web.config

Jillzhang.Messaging.Client WCF客戶端,一個Console應用程序 OnewayProxy.cs

 

NormalJobProxy.cs

DuplexProxy.cs

MyCallback.cs

Program.cs

app.config

下面就看下如何定義消息交換模式為one-way的Contract接口

而IOneWayJob的實現類代碼為:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;

namespace Jillzhang.Messaging.Service
{
  public class OneWayJob : IOneWayJob
  {
    public void Do(string jobName)
    {
      System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
      watcher.Start();
      System.Threading.Thread.Sleep(1000);
      Console.WriteLine("服務" + AppDomain.CurrentDomain.FriendlyName + "執行任務:" + jobName);
      watcher.Stop();
    }
  }
}

Request/reply的Contract接口定義如下:

而INormalJob的實現代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;

namespace Jillzhang.Messaging.Service
{
  public class NormalJob:INormalJob
  {
    public string Do(string jobName)
    {
      try
      {       
        System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
        watcher.Start();
        System.Threading.Thread.Sleep(1000);
        Console.WriteLine("服務" + AppDomain.CurrentDomain.FriendlyName + "執行任務:" + jobName);
        watcher.Stop();       
        return "成功";
      }
      catch
      {
        return "失敗";
      }
    }
  }
}

Duplex的交換模式需要現定義Callback的Contract接口,如下:

而服務端的Contract接口為:

Duplex的Contract實現為:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;
using System.ServiceModel;

namespace Jillzhang.Messaging.Service
{
  [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
  public class Job:IJob
  {
    public string Do(string jobName)
    {
      try
      {
        ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
        System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
        watcher.Start();
        System.Threading.Thread.Sleep(1000);
        Console.WriteLine("服務" + AppDomain.CurrentDomain.FriendlyName + "執行任務:" + jobName);
        watcher.Stop();
        callback.Done((int)watcher.ElapsedMilliseconds);
        return "成功";
      }
      catch
      {
        return "失敗";
      }
    }
  }
}

下面,我們來看一下,如何創建承載服務的應用程序,首先在app.config做如下配置

而Host的代碼如下:

而客戶端的配置文件,如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
   <bindings>
      <netTcpBinding>
        <binding name="netTcpBinding" />
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="net.tcp://localhost:6987/Service/duplex" binding="netTcpBinding"
        bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.IJob"
        name="NetTcpBinding">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="net.tcp://localhost:6987/Service/oneway" binding="netTcpBinding"
        bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.IOneWayJob"
        name="NetTcpBinding">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
     <endpoint address="net.tcp://localhost:6987/Service/normal" binding="netTcpBinding"
     bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.INormalJob"
     name="NetTcpBinding">
      <identity>
       <dns value="localhost" />
      </identity>
     </endpoint>
    </client>
  </system.serviceModel>
</configuration>

需要注意的是:在設定Duplex模式時,如果服務端采用的是WsDualHttpBinding,而不是本文中的NetTcpBinding,最好指定以下clientBaseAddress,默認情況下,clientBaseAddress會嘗試用80端口,可通常情況80端口都是被占用,你需要設置一個其他端口。

因為回調的Contract實現是在客戶端的,所以需要在客戶端實現1個ICallback實現,代碼如下:

下面是客戶端調用的代碼:

客戶端代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;
namespace Jillzhang.Messaging.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            p.OneWayCall();
            p.NormalCall();
            p.DuplexCall();
            Console.Read();
        }
        void DuplexCall()
        {
            try
            {
                MyCallback callback = new MyCallback();
                IJob ws = new JobClient(new System.ServiceModel.InstanceContext(callback));
                Console.WriteLine("--------------------Duplex Calls ---------------------------");
                Console.WriteLine("開始調用服務");
                string result = ws.Do("duplex job");
                Console.WriteLine("收到返回信息:" + result);
                Console.WriteLine("-------------------------------------------------------------");
                Console.WriteLine("\r\n\r\n\r\n");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        void OneWayCall()
        {
            try
            {
                Console.WriteLine("-----------------------One-Way Calls-----------------------");
                IOneWayJob ws = new OneWayJobClient();
                ws.Do("one-way job");
                Console.WriteLine("請求完成!");
                Console.WriteLine("-------------------------------------------------------------");
                Console.WriteLine("\r\n\r\n\r\n");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        void NormalCall()
        {
            try
            {
                Console.WriteLine("-----------------------Request/Reply Calls-----------------------");
                INormalJob ws = new NormalJobClient();
                string result = ws.Do("request/reply job");
                Console.WriteLine("請求完成,返回結果:"+result);
                Console.WriteLine("-------------------------------------------------------------");
                Console.WriteLine("\r\n\r\n\r\n");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

首先運行服務承載程序Jillzhang.Messaging.Host,然後運行客戶端

會產生如下的結果:

服務端運行解圖

客戶端運行解圖:

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved