一. 簡介
二. 實例
今天要講解的是一個WCF實現的小功能——圖片傳輸,實現服務器端向客戶端發送圖片,客戶端顯示接收到的圖片....閑話少說...直接切入正題......
WCF編程模式基於兩個實體:WCF服務端、WCF客戶端,但是這兩者在通信的時候是基於某種契約,只有同時滿足契約的條件的雙方才能通信,這時就有了WCF服務....所以我們首先從契約入手.....
契約是基於一個定義服務與客戶端之間協定的接口,它是用ServiceContractAttribute 屬性來進行標記的;服務簡單說,就是繼承並實現了接口中定義的方法的一個類
首先貼上整個框架結構,如圖:
【模塊介紹】
Holyknihgt.PicPass.Client_SendPic:發送圖片(客戶端)
Holyknihgt.PicPass.Contract:定義契約(接口)
Holyknihgt.PicPass.ReceivePic:接受圖片(客戶端)
首先從契約入手,貼上代碼:

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特性
接下去就是對這個接口的實現,即服務,貼上代碼:

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的問題,一開始在發送的方法中加上了
接下來貼上服務端的代碼:

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文件,點擊添加
然后選擇該服務即可,然后點擊下一步,會自動為你匹配好對應的契約,直接下一步,到基址配置,如圖:
輸入正確的基址地址,服務就算建好了.....然后添加服務行為,如圖:
然后選擇添加,然后選擇對應的服務元數據項
然后雙擊該元數據項,進行配置,配置如圖:
最后再在主機中添加該配置好的行為就可以了,,如圖:
這樣,我們的配置文件算是配好了,,貼上配置文件結果:
服務端就告一段落了,接下來搭建我們的客戶端,首先搭建發送端,貼上代碼:

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方法:

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