WCF開山篇__圖片傳輸html
一. 簡介編程
二. 實例安全
今天要講解的是一個WCF實現的小功能——圖片傳輸,實現服務器端向客戶端發送圖片,客戶端顯示接收到的圖片....閒話少說...直接切入正題...... 服務器
WCF編程模式基於兩個實體:WCF服務端、WCF客戶端,可是這二者在通訊的時候是基於某種契約,只有同時知足契約的條件的雙方纔能通訊,這時就有了WCF服務....因此咱們首先從契約入手.....網絡
契約是基於一個定義服務與客戶端之間協定的接口,它是用ServiceContractAttribute 屬性來進行標記的;服務簡單說,就是繼承並實現了接口中定義的方法的一個類app
首先貼上整個框架結構,如圖:框架
【模塊介紹】tcp
Holyknihgt.PicPass.Client_SendPic:發送圖片(客戶端)函數
Holyknihgt.PicPass.Contract:定義契約(接口)工具
Holyknihgt.PicPass.ReceivePic:接受圖片(客戶端)
首先從契約入手,貼上代碼:
using System.ServiceModel; using System.IO; namespace HolyKnight.PicPass.Contract { [ServiceContract] public interface IContracts { //獲取圖片 [OperationContract] Stream GetPic(); //發送圖片 [OperationContract] void SendPic(Stream picPass); } }
契約中主要定義了兩個方法:獲取圖片方法(GetPic) 和 發送圖片方法(SendPic),並添加了對應的ServiceContractAttribute特性
接下去就是對這個接口的實現,即服務,貼上代碼:
using HolyKnight.PicPass.Contract; namespace HolyKnight.PicPass.Service { public class MyService:IContracts { //定義一個靜態內存流 用於存儲圖片 public static Stream picStream = new MemoryStream(); //獲取圖片 public Stream GetPic() { //實例化一個內存流對象 MemoryStream ms = new MemoryStream(); //設置靜態內存流的位置爲0 爲了後面進行拷貝操做時能夠從頭開始拷貝內容 //【這裏的Position不同了,用了多態的思想,子類已經重寫了父類的Position屬性】 picStream.Position = 0; //把內存流中的內容拷貝到當前內存流中 picStream.CopyTo(ms); //返回內存流對象 return ms; } //發送圖片 public void SendPic(Stream picPass) { //【出錯】:設置位置爲0 -- 由於stream類的position屬性是抽象屬性的 不能直接複製使用 //picPass.Position = 0; //【再次報錯】--考慮不全:下面這句代碼沒指定,因此發送的時候沒有把Position設爲0 而接受的時候一直是從0開始接受 因此一直接受不到後面發送的圖片 picStream.Position = 0; //拷貝到靜態內存流中 保存 picPass.CopyTo(picStream); }
這塊對我來講是"重災區",由於我一開始在這個地方出了兩次一樣的錯誤,,悲哀,,錯誤已經在註釋中寫明瞭,,,就是Position的問題,一開始在發送的方法中加上了
接下來貼上服務端的代碼:
class Hosting { static void Main(string[] args) { //設置 綁定方式爲Tcp NetTcpBinding tcpBind = new NetTcpBinding(); //設置 用於存儲消息的緩衝區的 最大大小 tcpBind.MaxBufferSize = 217736174; //設置服務器 使用流式處理模式 傳輸消息 tcpBind.TransferMode = TransferMode.Streamed; //設置綁定能夠處理的最大接受消息大小 tcpBind.MaxReceivedMessageSize = 217736174; tcpBind.Security.Mode = SecurityMode.None; //BasicHttpBinding httpBind = new BasicHttpBinding(); //httpBind.MaxBufferSize = 1234567; //httpBind.TransferMode = TransferMode.Streamed; //httpBind.MaxReceivedMessageSize = 1234567; //發佈 //新建服務主機 using (ServiceHost host = new ServiceHost(typeof(MyService))) { //爲主機添加服務終結點 配置ABC A:address B:bind C:contract 而且基址爲net.tcp地址 host.AddServiceEndpoint(typeof(IContracts), tcpBind, "net.tcp://192.168.29.223:8000/MyService"); //控制服務行爲 ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>(); //若是爲空 則實例化一個對象 並設置對應屬性 if (behavior == null) { behavior = new ServiceMetadataBehavior(); behavior.HttpGetEnabled = true; //這裏的地址爲http地址 且端口號不能和上面定義的同樣 不然會端口占用 behavior.HttpGetUrl = new Uri("http://192.168.29.223:9999/MyService/Medata"); //將該行爲添加到主機 host.Description.Behaviors.Add(behavior); } host.Opened += delegate { Console.WriteLine("圖片傳輸服務已啓動,按任意鍵關閉服務....."); }; //打開通訊 host.Open(); Console.ReadKey(); } }
服務端很簡單,大體過程爲先添加一個服務主機,參數爲服務的類型;而後爲該服務主機添加一個服務終結點,服務終結點要配置三個信息即A,B,C,其中A就是Address,表示基址地址,因爲這裏是經過tcp傳輸,因此這裏的地址爲tcp地址,並設定端口號;B就是Binding,表示綁定的類型和方式;C就是Contract,表示終結點定義的契約,這三點配置好了終結點算配置好了,而後添加一個服務行爲behavior,首先查詢主機中是否含有了服務行爲,若是沒有則添加一個行爲,並設置該行爲的HttpGetEnabled屬性爲true,表示能夠經過Http來檢索發佈的元數據,並設置元數據發佈的地址HttpGetUrl,注意這裏的地址是Http地址,並設定端口,這裏的端口不能和上面的基址的端口相同,不然會端口占用,而後將該行爲添加到主機就完成了配置了,而後利用Opened事件,用匿名函數來打印一段信息,而後打開通訊,這樣服務器端就配置完成了,代碼上的語句都作了相應的註釋....
固然服務端咱們也能夠不用手寫代碼的方式,咱們能夠選擇使用配置文件,下面講講使用配置文件來搭建主機
首先咱們爲主機模塊添加一個應用程序配置文件app.config,而後利用vs工具中的wcf配置工具打開該配置文件,如圖:
而後進去配置界面,首先新建一個服務
而後進入服務配置界面,選擇對應的服務的dll文件,點擊添加
而後選擇該服務便可,而後點擊下一步,會自動爲你匹配好對應的契約,直接下一步,到基址配置,如圖:
輸入正確的基址地址,服務就算建好了.....而後添加服務行爲,如圖:
而後選擇添加,而後選擇對應的服務元數據項
而後雙擊該元數據項,進行配置,配置如圖:
最後再在主機中添加該配置好的行爲就能夠了,,如圖:
這樣,咱們的配置文件算是配好了,,貼上配置文件結果:
服務端就告一段落了,接下來搭建咱們的客戶端,首先搭建發送端,貼上代碼:
namespace HolyKnight.PicPass.Client_SendPic { public partial class Send : Form { public Send() { InitializeComponent(); } //選擇圖片事件 private void btn_selectPic_Click(object sender, EventArgs e) { string fileName = ""; OpenFileDialog open = new OpenFileDialog(); if (open.ShowDialog() == DialogResult.OK) { //文本框顯示圖片路徑 fileName = open.FileName; //圖片框顯示圖片 pb_Pic.Load(fileName); } else { return; } //新建一個文件流 讀取圖片信息 FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); //新建一個內存流 用於存放圖片 Stream strPic = new MemoryStream(); //設置位置爲0 從頭開始拷貝 fs.Position = 0; //拷貝 fs.CopyTo(strPic); //網絡地址 EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService"); #region TCP //設置 綁定方式爲Tcp NetTcpBinding tcpBind = new NetTcpBinding(); //設置 用於存儲消息的緩衝區的 最大大小 tcpBind.MaxBufferSize = 217736174; //設置服務器 使用流式處理模式 傳輸消息 tcpBind.TransferMode = TransferMode.Streamed; //設置綁定能夠處理的最大接受消息大小 tcpBind.MaxReceivedMessageSize = 217736174; tcpBind.Security.Mode = SecurityMode.None; #endregion //BasicHttpBinding httpBind = new BasicHttpBinding(); //httpBind.MaxBufferSize = 1234567; //httpBind.TransferMode = TransferMode.Streamed; //httpBind.MaxReceivedMessageSize = 1234567; //建立通訊通道 IContracts client = ChannelFactory<IContracts>.CreateChannel(tcpBind, address); //設置位置爲0 strPic.Position = 0; //調用SendPic方法 發送圖片 client.SendPic(strPic); } } }
代碼語句都有對應註釋,就不加贅述了,思路就是先獲取到本地圖片,而後轉換成內存流,再鏈接到主機,建立代理類,經過代理類調用服務中的SendPic方法,實現發送圖片
再貼上接收端的代碼,思路同發送端雷同,調用服務的GetPic方法:
namespace HolyKnight.PicPass.ReceivePic { public partial class Receive : Form { public Receive() { InitializeComponent(); } public void ShowPic() { //網絡地址 EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService"); #region TCP //設置 綁定方式爲Tcp NetTcpBinding tcpBind = new NetTcpBinding(); //設置 用於存儲消息的緩衝區的 最大大小 tcpBind.MaxBufferSize = 217736174; //設置服務器 使用流式處理模式 傳輸消息 tcpBind.TransferMode = TransferMode.Streamed; //設置綁定能夠處理的最大接受消息大小 tcpBind.MaxReceivedMessageSize = 217736174; //設置無安全性檢驗 tcpBind.Security.Mode = SecurityMode.None; #endregion #region HTTP //BasicHttpBinding httpBind = new BasicHttpBinding(); //httpBind.MaxBufferSize = 1234567; //httpBind.TransferMode = TransferMode.Streamed; //httpBind.MaxReceivedMessageSize = 1234567; #endregion //獲取服務代理類 IContracts prox = ChannelFactory<IContracts>.CreateChannel(tcpBind, address); //用死循環持續監聽 while (true) { //經過調用GetPic()方法獲取圖片 Stream strPic = prox.GetPic(); //新建一個內存流 MemoryStream ms = new MemoryStream(); //將獲取到得圖片拷貝到內存流 strPic.CopyTo(ms); if (ms.Length == 0) { //線程阻塞300毫秒 System.Threading.Thread.Sleep(300); continue; } //將圖片信息加載到位圖 Bitmap bm = new Bitmap(ms); //顯示圖片 pb_showPic.Image = bm; //線程阻塞 System.Threading.Thread.Sleep(300); } } private void Form1_Load(object sender, EventArgs e) { //利用線程監聽GetPic方法 Thread threadPic = new Thread(ShowPic); threadPic.IsBackground = true; threadPic.Start(); } } }
這樣,咱們服務端,客戶端的配置都完成了,接下來看看演示效果......直接上圖:
首先打開服務:
而後再開啓兩個客戶端,首先貼上客戶端頁面:
發送端:
接收端:
而後開始發送圖片:
測試:咱們在客戶端更換圖片
OK。。。運行,測試都沒有問題.....那麼這個例子就算完成了..............