十五天精通WCF——第四天 你一定要明白的通訊單元Message

一線碼農發表於2015-06-13

  轉眼你已經學了三天的wcf了,是不是很好奇wcf在傳輸層上面到底傳遞的是個什麼鳥毛東西呢???應該有人知道是soap,那soap這叼毛長得是什麼

樣呢?這一篇我們來揭開答案。。。

 

一:soap到底長成什麼樣子

  為了能看清soap長的啥樣,我可以用強大的Fiddler來監視一下,突然好激動啊!!!

1.Server

 1         static void Main(string[] args)
 2         {
 3             ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:19200"));
 4 
 5             host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");
 6 
 7             //公佈後設資料
 8             host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
 9 
10             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
11 
12             host.Open();
13 
14             Console.WriteLine("服務已經開啟。。。");
15 
16             Console.Read();
17         }

2.Client

1             ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
2 
3             var client = factory.CreateChannel();
4 
5             client.Update("王八蛋");

 

 

現在我想你大概看清楚了這玩意是個麼樣子,一個建立在xml上面的一種訊息格式,根元素是envelope,我知道這叼毛翻譯過來就是“信封”,所以就有了”封頭“

和”封體”,就是s:Header 和 s:Body,從這個soap中你可以看到它忽略了header,然後我們繼續往下看,還記得Update的意思嗎???如果你讀懂了上一篇,

你應該知道這是一個Action,也就是所謂的input訊息。與之對應的就是UpdateResponse這個output訊息,對吧,還記得xmlns="http://tempuri.org/">嗎?

它就是IHomeService的預設名稱空間,對吧。。。

下一個我們關注的是Update這個Action中的<str>這個,你也看得到,這個就是上圖中Update方法中的str引數,最後我們來看一下UpdateResponse中

的<UpdateResult xmlns:a="http://schemas.datacontract.org/2004/07/MyService,不知道你是否還記得它就是WSDL中關於Student的XSD結

構,看下圖:

 

好了,wcf中的soap結構我們也大概瞭解了一下,不知道有沒有引發你對soap更深入的思考呢???

 

二:對soap的更深入思考

  通過fiddler觀察,你應該也明白了,不管是客戶端還是服務端,wcf的高層封裝都是僅僅拿出了Envelope中的body節點,而其他節點對我們來說好像並

沒有什麼卵用,比如我說的Header節點,這麼說來,Header是不是有點浪費呢???那下面有一個問題來了,wcf在底層用什麼來構造訊息的呢???下面

我們大概找下client端的原始碼。。。

 

通過上面的圖,你現在應該也知道了在.net中其實tmd的就是message構造的,所以我想告訴你的是:既然wcf在底層也是用message來構造的,何不我自己

就來構造message訊息呢???豈不美哉???這樣我就可以隨意操作message,對吧。。。不然wcf這個高層封裝的叼毛,對我來說就是一種束縛。。。因

為我已經知道了service公佈的wsdl,所以我可以輕鬆構造message。。。

 

三:用message來呼叫Server端

  廢話不多說,構造message你一定要知道下圖中的三點:(input這個Action,契約方式 和 服務地址)。

 

好了,下面我先來構造資料契約,指定服務契約的名稱空間 和 Action在Soap中的名稱

1     [DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
2     class Test
3     {
4         [DataMember]
5         public string str { get; set; }
6     }

然後,我把這個資料契約塞到envelope中的body中,如下:

 1             BasicHttpBinding bingding = new BasicHttpBinding();
 2 
 3             BindingParameterCollection param = new BindingParameterCollection();
 4 
 5             var u = new Test() { str = "王八蛋" };
 6 
 7             Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);
 8 
 9             IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
10 
11             factory.Open();
12 
13             IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
14 
15             channel.Open();
16 
17             var result = channel.Request(request);
18 
19             channel.Close();
20 
21             factory.Close();

接下來,我們跑起來看一下,效果咋樣。。。

 

看沒看到,這個就是我手工構造的Message,是不是太帥了。。。哈哈,太帥的應該在後面,剛才也說了,既然大家玩的都是Message,而你這個幾把wcf卻僅僅把

我的message.body拿出來了,那乾脆我直接在契約方法中加message豈不是更好麼???自由操作Message還有個什麼好處呢??當然啦,我可以在Message的

Header中加一些引數token,client的ip地址,client的身份,client的時間等等這些統計資訊,對吧。。。這樣才是最帥的,好了,說幹就幹,我們修改下server端的

契約方法,只用來接受Message。

 

server端:

 1     public class HomeService : IHomeService
 2     {
 3         public Message Update(Message message)
 4         {
 5             var header = message.Headers;
 6 
 7             var ip = header.GetHeader<string>("ip", string.Empty);
 8 
 9             var currentTime = header.GetHeader<string>("currenttime", string.Empty);
10 
11             //這個就是牛逼的 統計資訊。。。
12             Console.WriteLine("客戶端的IP=" + ip + " 當前時間=" + currentTime);
13 
14             return Message.CreateMessage(message.Version, message.Headers.Action + "Response", "等我吃完肯德基,再打死你這個傻逼!!!");
15         }
16     }

 

client端:

 1 namespace ConsoleApplication1
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             BasicHttpBinding bingding = new BasicHttpBinding();
 8 
 9             BindingParameterCollection param = new BindingParameterCollection();
10 
11             var u = new Test() { str = "王八蛋" };
12 
13             Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);
14 
15             //在header中追加ip資訊
16             request.Headers.Add(MessageHeader.CreateHeader("ip", string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString()));
17             request.Headers.Add(MessageHeader.CreateHeader("currenttime", string.Empty, DateTime.Now));
18 
19             IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
20 
21             factory.Open();
22 
23             IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
24 
25             channel.Open();
26 
27             var result = channel.Request(request);
28 
29             channel.Close();
30 
31             factory.Close();
32         }
33     }
34 
35     [DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
36     class Test
37     {
38         [DataMember]
39         public string str { get; set; }
40     }
41 }

 

然後我們用Fiddler監視一下結果:

 

現在一切都如我所願,好了,我想你也大概明白了這個神奇的message,也不要忘了它就是wcf的基本通訊單元,我要去吃肯德基了。。。。。。

 

相關文章