WCF學習之旅—TCP雙工模式(二十一)

来源:http://www.cnblogs.com/chillsrc/archive/2016/08/16/5776049.html
-Advertisement-
Play Games

在一個基於面向服務的分散式環境中,藉助一個標準的、平臺無關的通信協議,使各個服務通過SOAP Message實現相互之間的交互。這個交互的過程實際上就是信息交換的過程。WCF支持不同形式的信息交換,我們把這稱之為信息交換模式(Message Exchange Pattern(簡稱MEP),下同), ... ...


                WCF學習之旅—請求與答覆模式和單向模式(十九)

                WCF學習之旅—HTTP雙工模式(二十)

 

五、TCP雙工模式

     上一篇文章中我們學習了HTTP的雙工模式,我們今天就學習一下TCP的雙工模式。

      在一個基於面向服務的分散式環境中,藉助一個標準的、平臺無關的通信協議,使各個服務通過SOAP Message實現相互之間的交互。這個交互的過程實際上就是信息交換的過程。WCF支持不同形式的信息交換,我們把這稱之為信息交換模式(Message Exchange Pattern(簡稱MEP),下同), 常見的MEP包括: 請求/答覆,單向模式和雙工模式。通過採用雙工的MEP,我們可以實現在服務端調用客戶端的操作。雖然WCF為我們實現底層的通信細節,使得我們把精力轉移到業務邏輯的實現,進行與通信協議無關的編程,但是對通信協議的理解有利於我們根據所處的具體環境選擇一個合適的通信協議。說到通信協議, WCF 經常使用的是以下4個:Http,TCP,Named Pipe,MSMQ。

      我們用上一文章( WCF學習之旅—HTTP雙工模式(二十))中的示例,進行一下修改,變成一個TCP雙向通信的WCF服務應用程式。下麵直接上代碼。


1
Contract

 

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; 

namespace Contracts
{

    // 註意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼和配置文件中的介面名“IBookService”。
    [ServiceContract(CallbackContract = typeof(ICallback))]
    public interface IBookService
    {

       /// <summary>
       /// 請求與答覆模式,預設模式
       /// </summary>
       /// <param name="Id">書籍ID</param>
       /// <returns></returns>
        [OperationContract]
        string GetBook(string Id);
        /// <summary>

        /// 單工模式,顯示名稱
        /// </summary>
        /// <param name="name">書籍名稱</param>
        [OperationContract(IsOneWay = true)]
        void ShowName(string name);
        /// <summary>
        /// 雙工模式,顯示名稱
        /// </summary>
        /// <param name="name">書籍名稱</param>
        [OperationContract(IsOneWay = true)]
        void DisplayName(string name);
 

    }

}

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; 

namespace Contracts
{ 

    public interface ICallback
    {

        [OperationContract(IsOneWay = true)]
        void DisplayResult(string result);

    }
}

 

 

2WcfServiceLib

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 

namespace WcfServiceLib
{

    // 註意: 使用“重構”菜單上的“重命名”命令,可以同時更改代碼、svc 和配置文件中的類名“BookService”。

    // 註意: 為了啟動 WCF 測試客戶端以測試此服務,請在解決方案資源管理器中選擇 BookService.svc 或 BookService.svc.cs,然後開始調試。
    public class BookService : IBookService
    {

        /// <summary>
        /// 請求與答覆模式,預設模式
        /// </summary>
        /// <param name="Id">書籍ID</param>
        /// <returns></returns>
        public string GetBook(string Id)
        {

            System.Threading.Thread.Sleep(20000);
            int bookId = Convert.ToInt32(Id);
            Books book = SetBook(bookId);
            string xml = XMLHelper.ToXML<Books>(book);
            return xml; 

        }

 

        public Books SetBook(int Id)
        {

            Books book = new Books();
            book.BookID = Id;
            book.AuthorID = 1;
            book.Category = "IBM";
            book.Price = 39.99M;
            book.Numberofcopies = 25;
            book.Name = "DB2資料庫性能調整和優";
            book.PublishDate = new DateTime(2015, 2, 23);
            return book;
        }

        /// <summary>
        /// 單工模式,顯示名稱
        /// </summary>
        /// <param name="name">名稱</param>
        public void ShowName(string name)
        {

            string result = string.Format("書籍名稱:{0},日期時間{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            Console.WriteLine("\r\n" + result);          

        }

        /// <summary>
        /// 雙工模式,回調顯示結果
        /// </summary>
        /// <param name="name">名稱</param>
        public void DisplayName(string name)
        {
            string result=string.Format("書籍名稱:{0},日期時間{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            Console.WriteLine("\r\n" + result);
            ICallback call = OperationContext.Current.GetCallbackChannel<ICallback>();
            call.DisplayResult("回調客戶端---"+result);

        }

    }

}

 

      在服務端,通過OperationContext.Current.GetCallbackChannel來獲得客戶端指定的CallbackContext 實例,進而調用客戶端的操作。

3Hosting

宿主配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />

  </startup>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true" logKnownPii="false" logMalformedMessages="true"
        logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
      <endToEndTracing propagateActivity="true" activityTracing="true"
        messageFlowTracing="true" />
    </diagnostics>
 

    <services>
      <service  name="WcfServiceLib.BookService">
        <endpoint address="net.tcp://127.0.0.1:9999/BookService" binding="netTcpBinding"
        contract="Contracts.IBookService" />

      </service>
    </services>
  </system.serviceModel>
</configuration>

 

 

      我們通過netTcpBinding來模擬基於TCP的雙向通信。代碼如下:

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using WcfServiceLib;

 

namespace ConsoleHosting
{

    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("輸入啟動方式,C--Code A -- App.config方式!");
            string key = Console.ReadLine();
            switch (key)
            {
                case "C":
                    StartByCode();
                    break;
                case "A":
                    StartByConfig();
                    break;
                default:
                    Console.WriteLine("沒有選擇啟動方式,使用預設方式");
                    StartByCode();
                    break;

            }
        }

        private static void StartByCode()
        {

            //創建宿主的基地址
            Uri baseAddress = new Uri("http://localhost:8080/BookService");
            //創建宿主
            using (ServiceHost host = new ServiceHost(typeof(BookService), baseAddress))
            {

                //向宿主中添加終結點
                host.AddServiceEndpoint(typeof(IBookService), new WSDualHttpBinding(), baseAddress);
                if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)

                {
                    //將HttpGetEnabled屬性設置為true
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = baseAddress;
                    //將行為添加到Behaviors中 

                    host.Description.Behaviors.Add(behavior);
                    //打開宿主

                    host.Opened += delegate
                    {
                        Console.WriteLine("BookService控制台程式寄宿已經啟動,HTTP監聽已啟動....,按任意鍵終止服務!");
                    };

                    host.Open();
                    //print endpoint information                  

                    Console.ForegroundColor = ConsoleColor.Yellow;
                    foreach (ServiceEndpoint se in host.Description.Endpoints)
                    {
                        Console.WriteLine("[終結點]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-綁定]: {2} \r\n\t [C-協定]: {3}",
                     se.Name, se.Address, se.Binding.Name, se.Contract.Name);
                    }
                    Console.Read();
                }
            }

        }

        private static void StartByConfig()
        {
            using (ServiceHost host = new ServiceHost(typeof(BookService)))
            {
                host.Opened += delegate
                {

                    Console.WriteLine("BookService控制台程式寄宿已經啟動,TCP監聽已啟動....,按任意鍵終止服務!");
                };

                host.Open();
                //print endpoint information                 

                Console.ForegroundColor = ConsoleColor.Yellow;
                foreach (ServiceEndpoint se in host.Description.Endpoints)
                {
                    Console.WriteLine("[終結點]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-綁定]: {2} \r\n\t [C-協定]: {3}",
                 se.Name, se.Address, se.Binding.Name, se.Contract.Name);
 
                }

                Console.Read();
            }

        }

    }

}

 

 

 

4.客戶端:

    配置文件中的信息進行修改:

  <system.serviceModel>
        <client>
           <endpoint address="net.tcp://localhost:9999/BookService" binding="netTcpBinding"

                 bindingConfiguration="" contract="Contracts.IBookService"

                 name="BookServiceEndpoint" />
        </client>
    </system.serviceModel>

 

      接下來實現對雙工服務的調用,下麵是相關的配置和托管程式。在服務調用程式中,通過 DuplexChannelFactory<TChannel>創建服務代理對 象,DuplexChannelFactory<TChannel>和ChannelFactory<TChannel>的功能 都是一個服務代理對象的創建工廠,不過DuplexChannelFactory<TChannel>專門用於基於雙工通信的服務代理的創 建。在創建DuplexChannelFactory<TChannel>之前,先創建回調對象,並通過InstanceContext對回 調對象進行包裝。代碼如下:

private void btnTcpDuplex_Click(object sender, EventArgs e)
        { 

            DuplexChannelFactory<IBookService> channelFactory = new DuplexChannelFactory<IBookService>(instanceContext, "BookServiceEndpoint");
            IBookService client = channelFactory.CreateChannel();

                       

            //在BookCallBack對象的mainThread(委托)對象上搭載兩個方法,線上程中調用mainThread對象時相當於調用了這兩個方法。 

            textBox1.Text += string.Format("開始調用wcf服務:{0}\r\n\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

            client.DisplayName("TCP---科學可以這樣看叢書");

            textBox1.Text += string.Format("\r\n\r\n調用結束:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }

 

       在創建 DuplexChannelFactory< IBookService >中,指定了Callback Context Instance: 一個實現了Callback Contract的BookCallBack對象。該對象在Service中通過 OperationContext.Current.GetCallbackChannel<ICallback>()獲得。

通過運行程式之後的結果如下圖:

 

 

  2. 基於Http的雙向通訊V.S.基於TCP的雙向通訊

       由於Http和TCP在各自協議上的差異,他們實現雙向通信的髮式是不同的。

       Http是一個應用層的協議,它的主要特征就是無連接和無狀態。它採用傳統的“請求/回覆”的方式進行通信,客戶端發送Http Request請求服務端的某個資源,服務端接收到該Http Request, 回發對應的Http Response。當客戶端接收到對應的Response,該連接就會關閉。也就是說客戶端和服務端的 連接僅僅維持在發送Request到接收到Response這一段時間內。同時,每次基於Http的 連接是相互獨立,互不相干的,當前連接無法獲得上一次連接的狀態。為了保存調用的的狀態信 息,ASP.NET通過把狀態信息保存在服務端的Session之中,具體的做法是:ASP.NET為每個Session創建一個 Unique ID,與之關聯一個HttpSessionState對象,並把狀態信息保存在記憶體中或者持久的存儲介質(比如SQL Server)中。而WCF則採用另外的方式實現對Session的支持:每個Session關聯到某個Service Instance上。

       我們來講一下HTTP雙向通信的過程,當客戶端通過HTTP請求調用WCF服務之前,會有一個終結點在客戶端被創建,用於監聽服務端對它的Request。客戶端對 WCF服務的調用會建立一個客戶端到服務端的連接,當WCF服務在執行操作過程中需要回調對應的客戶端,實際上會建立另一個服務端到客戶端的Http 連接。雖然我們時候說WCF為支持雙向通信提供一個雙工通道,實際上這個雙工通道是由兩個HTTP連接組成的。

      再來看一下TCP的雙向通信的過程,對於TCP傳輸層協議,它則是一個基於連接的協議,在正式進行數據傳輸的之前,必須要在客戶端和服務端之間建立一個連接,連接的建立通過經典的“3次握手”來實現。TCP天生就具有雙工的特性,也就是說當連接 被創建之後,從客戶端到服務端,和從服務端到客戶端的數據傳遞都可以利用同一個連接來實現。對於WCF中的雙向通信,客戶端調用服務端,服務端回調客戶端的操作使用的都是同一個連接、同一個通道。所以基於TCP的雙工通信模式才是真正意義上的雙工通信模式。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、契約(介面):定義用戶實體類User、需要實現的服務 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Servi ...
  • 在Nuget管理包時,有可能A包的存在需要B包的支持,A包在安裝之前,需要先安裝B包,這就叫做依賴關係,而在NUGET里,確實有這種概念,比如大叔的Lind.DDD.Manager包,它就依賴於Lind.DDD,在你安裝Lind.DDD.Manager時,Nuget會檢測它是否有依賴關係,如果有,它 ...
  • String.Format("{0} world!","hello") //將輸出 hello world!,沒有問題,但是只要在第一個參數的任意位置加上一個大括弧:String.Format("{0} wo{rld!","hello") //就會產生一個異常,異常信息是:Input string ...
  • 根據總結,大概分為以下三種: 第一種: 用DataTable中的Compute方法。 例如:" 1*2*3 " 代碼如下: 第二種:利用javascript中的Eval方法解析.此方法需引入添加COM引用:Microsoft Sctipt Control 1.0 在使用過程中,若出現無法嵌入互操作類 ...
  • From窗體的關閉事件,避免和FormClosed混淆。 關於文章意義:方便自己以後查閱,這也是自己寫博客的主要原因 ...
  • 我們在平常開發過程中,在設計數據的時候,經常碰到數據類型選擇的問題,為了更快,更合適地選擇正確的數據類型,所以在這裡做個總結。 分類 sql server 數據類型 c# 數據類型 描述 應用場景 字元和字元串 char(n) ... ...
  • 1: 添加單元測試 2:打開單元測試類 關鍵點: 類上加上標記:[TestClass],方法上添加標記:[TestMethod],方法輸出使用:Assert.IsNotNull(s,"測試失敗"); 其中:Assert.IsNotNull()是單元測試判斷是否成功方法,還有其他方法,s為判斷對象," ...
  • 結論: > Socket 理論上 支持 只上行,或者 只下行。 > 心跳包 必須是 上下行的 —— 心跳包請求(上行) - 心跳包響應(下行)。 > 如果 長時間 只有單向鏈接(只發送不接受,或者 只接受不發送) —— 路由器 就會 丟棄 Socket數據。 > 心跳包 不是 必須的 —— 任意 上 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...