WCF開山篇__圖片傳輸


一.  簡介

Windows Communication Foundation(WCF)是由微軟發展的一組數據通信的應用程序接口,可以翻譯為Windows通訊接口,它是.NET框架的一部分,由 .NET Framework 3.0 開始引入,與 Windows Presentation Foundation及 Windows Workflow Foundation並行為新一代 Windows 操作系統以及 WinFX 的三個重大應用程序開發類庫。WCF由於集合了幾乎由.NET Framework所提供的通信方法,因此學習曲線比較陡峭,開發人員必須針對各個部分做深入了解,才能操控WCF來開發應用程序,通信的雙方的溝通方式由合約來約定,雙方所遵循的通信方法有綁定方法來約定。。。。。

 

二.  實例

今天要講解的是一個WCF實現的小功能——圖片傳輸,實現服務器端向客戶端發送圖片,客戶端顯示接收到的圖片....閑話少說...直接切入正題...... 

 

WCF編程模式基於兩個實體:WCF服務端、WCF客戶端,但是這兩者在通信的時候是基於某種契約,只有同時滿足契約的條件的雙方才能通信,這時就有了WCF服務....所以我們首先從契約入手.....

契約是基於一個定義服務與客戶端之間協定的接口,它是用ServiceContractAttribute 屬性來進行標記的;服務簡單說,就是繼承並實現了接口中定義的方法的一個類

首先貼上整個框架結構,如圖:

 

【模塊介紹】

Holyknihgt.PicPass.Client_SendPic:發送圖片(客戶端)

Holyknihgt.PicPass.Contract:定義契約(接口)

Holyknihgt.PicPass.Hosting:主機(服務端)

Holyknihgt.PicPass.ReceivePic:接受圖片(客戶端)

Holyknihgt.PicPass.Service:服務

 

首先從契約入手,貼上代碼:

契約
 1  using System.ServiceModel;
 2  using System.IO;
 3 
 4  namespace HolyKnight.PicPass.Contract
 5 {
 6     [ServiceContract]
 7      public  interface IContracts
 8     {
 9          // 獲取圖片
10          [OperationContract]
11         Stream GetPic();
12 
13          // 發送圖片
14          [OperationContract]
15          void SendPic(Stream picPass);
16 
17     }
18 }

 契約中主要定義了兩個方法:獲取圖片方法(GetPic) 發送圖片方法(SendPic),並添加了對應的ServiceContractAttribute特性

 

接下去就是對這個接口的實現,即服務,貼上代碼:

服務
 1  using HolyKnight.PicPass.Contract;
 2 
 3  namespace HolyKnight.PicPass.Service
 4 {
 5      public  class MyService:IContracts
 6     {
 7          // 定義一個靜態內存流 用於存儲圖片
 8           public  static Stream picStream =  new MemoryStream();
 9 
10          // 獲取圖片
11           public Stream GetPic()
12         {
13              // 實例化一個內存流對象
14              MemoryStream ms =  new MemoryStream();
15 
16              // 設置靜態內存流的位置為0 為了后面進行拷貝操作時可以從頭開始拷貝內容
17               // 【這里的Position不一樣了,用了多態的思想,子類已經重寫了父類的Position屬性】
18              picStream.Position =  0;
19 
20              // 把內存流中的內容拷貝到當前內存流中
21              picStream.CopyTo(ms);
22 
23              // 返回內存流對象
24               return ms;
25         }
26 
27          // 發送圖片
28           public  void SendPic(Stream picPass)
29         {
30              // 【出錯】:設置位置為0 --  因為stream類的position屬性是抽象屬性的 不能直接復制使用
31               // picPass.Position = 0;
32 
33               // 【再次報錯】--考慮不全:下面這句代碼沒指定,所以發送的時候沒有把Position設為0 而接受的時候一直是從0開始接受 所以一直接受不到后面發送的圖片
34              picStream.Position =  0;
35 
36              // 拷貝到靜態內存流中 保存
37              picPass.CopyTo(picStream);
38         }
39   

  這塊對我來說是"重災區",因為我一開始在這個地方出了兩次同樣的錯誤,,悲哀,,錯誤已經在注釋中寫明了,,,就是Position的問題,一開始在發送的方法中加上了

picPass.Position = 0;這句代碼,后來運行一直報內部錯誤,經過幾次調試之后終於發現了錯誤點,就是這個Position造成的,但一開始始終不明白為什么會報錯呢??我的想法是把picPass的Position設置為0,可以實現從頭開始拷貝,,,但事實證明我想錯了,,通過對Stream類轉到定義(F12)查看才發現了特殊的地方,因為Position是Abstract,是抽象屬性,不能直接對抽象屬性賦值.....這樣總算之后為什么錯了,,,當然獲取圖片方法中也有Position屬性,Stream picStream = new MemoryStream()也有Position,為什么這里不報錯呢,,,這就是運用了多態了,其實他們都是Stream的一個派生類,派生類重寫了父類的Position屬性了,有了自身的擴展了,所以可以對其賦值了,,,代碼中還會碰到FileStream類,也是同樣道理。。第二個錯誤就是我在發送方法中漏寫了picStream.Position = 0;沒了這句話,給我的結果是程序都可以跑起來,,但是我發送你第一張圖片的時候,接受端可以接到,但當我后面再次發送不同圖片的時候,我卻怎么都接不到了,頁面顯示的始終是第一張圖片,,為什么呢???而又不報錯,很難找錯,,終於,黃天不負有心人啊,,經過百般的調試之后終於發現了這個錯誤了,,但為什么會這樣呢??原來,當你發送完一張圖片的時候,由於圖片是存放到內存流里的,圖片大小不一樣,放到內存之后的Position也是不一樣的,隨着圖片的增加而增加,但我接收端接受的是Position=0處的圖片,所以只有第一張圖片的position為0的,故我只能接受到第一張圖片了,加了這句代碼之后,就是說我發送的時候是發送到Position=0的位置,接收的時候也是接收Position=0位置的圖片,這樣就沒有任何問題了,,好了,至此,"重災區"的兩個問題都解決了.....

 

接下來貼上服務端的代碼:

服務端
 1      class Hosting
 2     {
 3          static  void Main( string[] args)
 4         {
 5              // 設置 綁定方式為Tcp
 6              NetTcpBinding tcpBind =  new NetTcpBinding();
 7              // 設置 用於存儲消息的緩沖區的 最大大小
 8              tcpBind.MaxBufferSize =  217736174;
 9              // 設置服務器 使用流式處理模式 傳輸消息
10              tcpBind.TransferMode = TransferMode.Streamed;
11              // 設置綁定可以處理的最大接受消息大小
12              tcpBind.MaxReceivedMessageSize =  217736174;
13 
14             tcpBind.Security.Mode = SecurityMode.None;
15 
16              // BasicHttpBinding httpBind = new BasicHttpBinding();
17               // httpBind.MaxBufferSize = 1234567;
18               // httpBind.TransferMode = TransferMode.Streamed;
19               // httpBind.MaxReceivedMessageSize = 1234567;
20 
21               // 發布
22               // 新建服務主機
23               using (ServiceHost host =  new ServiceHost( typeof(MyService)))
24             {
25                  // 為主機添加服務終結點 配置ABC A:address B:bind C:contract  並且基址為net.tcp地址
26                  host.AddServiceEndpoint( typeof(IContracts), tcpBind,  " net.tcp://192.168.29.223:8000/MyService ");
27 
28                  // 控制服務行為
29                  ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
30                  // 如果為空 則實例化一個對象 並設置對應屬性
31                   if (behavior ==  null)
32                 {
33                     behavior =  new ServiceMetadataBehavior();
34                     behavior.HttpGetEnabled =  true;
35 
36                      // 這里的地址為http地址 且端口號不能和上面定義的一樣 否則會端口占用
37                      behavior.HttpGetUrl =  new Uri( " http://192.168.29.223:9999/MyService/Medata ");
38 
39                      // 將該行為添加到主機
40                      host.Description.Behaviors.Add(behavior);
41                 }
42 
43                 host.Opened +=  delegate
44                 {
45                     Console.WriteLine( " 圖片傳輸服務已啟動,按任意鍵關閉服務..... ");
46                 };
47 
48                  // 打開通信
49                  host.Open();
50 
51                 Console.ReadKey();
52             }
53         }

 服務端很簡單,大致過程為先添加一個服務主機,參數為服務的類型;然后為該服務主機添加一個服務終結點,服務終結點要配置三個信息即A,B,C,其中A就是Address,表示基址地址,由於這里是通過tcp傳輸,所以這里的地址為tcp地址,並設定端口號;B就是Binding,表示綁定的類型和方式;C就是Contract,表示終結點定義的契約,這三點配置好了終結點算配置好了,然后添加一個服務行為behavior,首先查詢主機中是否含有了服務行為,如果沒有則添加一個行為,並設置該行為的HttpGetEnabled屬性為true,表示可以通過Http來檢索發布的元數據,並設置元數據發布的地址HttpGetUrl,注意這里的地址是Http地址,並設定端口,這里的端口不能和上面的基址的端口相同,否則會端口占用,然后將該行為添加到主機就完成了配置了,然后利用Opened事件,用匿名函數來打印一段信息,然后打開通信,這樣服務器端就配置完成了,代碼上的語句都做了相應的注釋....

 

當然服務端我們也可以不用手寫代碼的方式,我們可以選擇使用配置文件,下面講講使用配置文件來搭建主機

首先我們為主機模塊添加一個應用程序配置文件app.config,然后利用vs工具中的wcf配置工具打開該配置文件,如圖:

 

 

 然后進去配置界面,首先新建一個服務

 

然后進入服務配置界面,選擇對應的服務的dll文件,點擊添加

 

然后選擇該服務即可,然后點擊下一步,會自動為你匹配好對應的契約,直接下一步,到基址配置,如圖:

 

輸入正確的基址地址,服務就算建好了.....然后添加服務行為,如圖:

 

然后選擇添加,然后選擇對應的服務元數據項

 

 

 然后雙擊該元數據項,進行配置,配置如圖:

 

 最后再在主機中添加該配置好的行為就可以了,,如圖:

 

 這樣,我們的配置文件算是配好了,,貼上配置文件結果:

 

 

服務端就告一段落了,接下來搭建我們的客戶端,首先搭建發送端,貼上代碼:

發送端
 1  namespace HolyKnight.PicPass.Client_SendPic
 2 {
 3      public  partial  class Send : Form
 4     {
 5          public Send()
 6         {
 7             InitializeComponent();
 8         }
 9 
10          // 選擇圖片事件
11           private  void btn_selectPic_Click( object sender, EventArgs e)
12         {
13              string fileName =  "";
14             OpenFileDialog open =  new OpenFileDialog();
15              if (open.ShowDialog() == DialogResult.OK)
16             {
17                  // 文本框顯示圖片路徑
18                  fileName = open.FileName;
19                  // 圖片框顯示圖片
20                  pb_Pic.Load(fileName);
21             }
22              else
23             {
24                  return;
25             }
26 
27              // 新建一個文件流 讀取圖片信息
28              FileStream fs =  new FileStream(fileName, FileMode.Open, FileAccess.Read);
29              // 新建一個內存流 用於存放圖片
30              Stream strPic =  new MemoryStream();
31              // 設置位置為0 從頭開始拷貝
32              fs.Position =  0;
33              // 拷貝
34              fs.CopyTo(strPic);
35 
36              // 網絡地址
37              EndpointAddress address =  new EndpointAddress( " net.tcp://192.168.29.223:8000/MyService ");
38 
39              #region TCP
40 
41              // 設置 綁定方式為Tcp
42              NetTcpBinding tcpBind =  new NetTcpBinding();
43              // 設置 用於存儲消息的緩沖區的 最大大小
44              tcpBind.MaxBufferSize =  217736174;
45              // 設置服務器 使用流式處理模式 傳輸消息
46              tcpBind.TransferMode = TransferMode.Streamed;
47              // 設置綁定可以處理的最大接受消息大小
48              tcpBind.MaxReceivedMessageSize =  217736174;
49 
50             tcpBind.Security.Mode = SecurityMode.None;
51 
52              #endregion
53 
54              // BasicHttpBinding httpBind = new BasicHttpBinding();
55               // httpBind.MaxBufferSize = 1234567;
56               // httpBind.TransferMode = TransferMode.Streamed;
57               // httpBind.MaxReceivedMessageSize = 1234567;
58 
59 
60               // 創建通信通道
61              IContracts client = ChannelFactory<IContracts>.CreateChannel(tcpBind, address);
62              // 設置位置為0
63              strPic.Position =  0;
64              // 調用SendPic方法 發送圖片
65              client.SendPic(strPic);
66             
67         }
68     }
69 }

代碼語句都有對應注釋,就不加贅述了,思路就是先獲取到本地圖片,然后轉換成內存流,再連接到主機,創建代理類,通過代理類調用服務中的SendPic方法,實現發送圖片

 

再貼上接收端的代碼,思路同發送端雷同,調用服務的GetPic方法:

接收端
 1  namespace HolyKnight.PicPass.ReceivePic
 2 {
 3      public  partial  class Receive : Form
 4     {
 5          public Receive()
 6         {
 7             InitializeComponent();
 8         }
 9 
10          public  void ShowPic()
11         {
12              // 網絡地址
13              EndpointAddress address =  new EndpointAddress( " net.tcp://192.168.29.223:8000/MyService ");
14 
15              #region TCP
16 
17              // 設置 綁定方式為Tcp
18              NetTcpBinding tcpBind =  new NetTcpBinding();
19 
20              // 設置 用於存儲消息的緩沖區的 最大大小
21              tcpBind.MaxBufferSize =  217736174;
22 
23              // 設置服務器 使用流式處理模式 傳輸消息
24              tcpBind.TransferMode = TransferMode.Streamed;
25 
26              // 設置綁定可以處理的最大接受消息大小
27              tcpBind.MaxReceivedMessageSize =  217736174;
28 
29              // 設置無安全性檢驗
30              tcpBind.Security.Mode = SecurityMode.None;
31 
32              #endregion
33 
34              #region HTTP
35 
36              // BasicHttpBinding httpBind = new BasicHttpBinding();
37               // httpBind.MaxBufferSize = 1234567;
38               // httpBind.TransferMode = TransferMode.Streamed;
39               // httpBind.MaxReceivedMessageSize = 1234567;
40 
41              #endregion
42 
43              // 獲取服務代理類
44              IContracts prox = ChannelFactory<IContracts>.CreateChannel(tcpBind, address);
45            
46              // 用死循環持續監聽
47               while ( true)
48             {
49                  // 通過調用GetPic()方法獲取圖片
50                  Stream strPic = prox.GetPic();
51 
52                  // 新建一個內存流
53                  MemoryStream ms =  new MemoryStream();
54 
55                  // 將獲取到得圖片拷貝到內存流
56                  strPic.CopyTo(ms);
57                  if (ms.Length ==  0)
58                 {
59                      // 線程阻塞300毫秒
60                      System.Threading.Thread.Sleep( 300);
61                      continue;
62                 }
63                  // 將圖片信息加載到位圖
64                  Bitmap bm =  new Bitmap(ms);
65 
66                  // 顯示圖片
67                  pb_showPic.Image = bm;
68 
69                  // 線程阻塞
70                  System.Threading.Thread.Sleep( 300);
71 
72             }
73 
74 
75         }
76 
77          private  void Form1_Load( object sender, EventArgs e)
78         {
79              // 利用線程監聽GetPic方法
80              Thread threadPic =  new Thread(ShowPic);
81             threadPic.IsBackground =  true;
82             threadPic.Start();
83         }
84     }
85 }

 

這樣,我們服務端,客戶端的配置都完成了,接下來看看演示效果......直接上圖:

 首先打開服務:

 

然后再開啟兩個客戶端,首先貼上客戶端頁面:

發送端:

 

接收端:

 

然后開始發送圖片:

 

測試:我們在客戶端更換圖片

 

 OK。。。運行,測試都沒有問題.....那么這個例子就算完成了..............

 

發發感慨:再過個十來天,本人也即將步入社會,投出人生中的第一份簡歷了,經歷過多少次的迷茫,努力,迷茫,努力之后,終於有一種去社會躍躍欲試的沖動了,看着周圍的人都找到了自己心儀的工作,漸漸的,這種沖動也越來越強烈了,,即使社會有很多的未知性,但相信社會始終是塊驗金石,會對你究竟的能力來個很好的鑒定,,或許你堅定的相信本身就是塊金子,只是一開始進入社會被埋沒了,未被發掘出來,就好比千里馬沒有伯樂的相識的無奈,所以只能沉默,沉默,,,但不要緊,相信自己,堅定自己的信念,如果人生是艘船,那么自己才是唯一的船長,奔着自己的信念,揚帆...起航....,畢竟,是金子,總會發光..........

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM