WCF開山篇__圖片傳輸

WCF開山篇__圖片傳輸html

 

一.  簡介編程

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 屬性來進行標記的;服務簡單說,就是繼承並實現了接口中定義的方法的一個類app

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

 

【模塊介紹】tcp

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

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

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

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

Holyknihgt.PicPass.Service:服務

 

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

 

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的問題,一開始在發送的方法中加上了

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位置的圖片,這樣就沒有任何問題了,,好了,至此,"重災區"的兩個問題都解決了.....

 

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

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。。。運行,測試都沒有問題.....那麼這個例子就算完成了..............

相關文章
相關標籤/搜索