1. 程式人生 > >20181102_WCF簡單雙工

20181102_WCF簡單雙工

  1. 使用管理員許可權開啟VS2017

  2. 建立一個空白解決方案:

3. 建立幾個基於.net  framework的類庫專案和一個基於.net Framework 的控制檯專案, 然後將類庫專案的class1.cs檔案刪除, 整體如下圖:

   

4. 為每個類庫新增對應的類檔案:

  a)         MyWCF.WCFSimpleDuplex.Interface新增兩個介面, ICalculatorService和ICallBack, 其中ICalculatorService表示要啟動的服務介面, 而ICallBack表示要回調客戶端時, 訪問的介面

  b)         ICalculatorService 程式碼如下:

using System.ServiceModel; 

namespace MyWCF.WCFSimpleDuplex.Interface
{
    /// <summary>
    /// 定義協議的時候, 同時需要定義一個回撥介面
    /// 示例中定義服務契約的同時定義一個回撥介面ICallBack
    /// 雙工中的函式不能帶返回值
    /// 如果你實在想要返回值, 那麼自己用out
    /// </summary>
    [ServiceContract(CallbackContract = typeof(ICallBack))]
    public interface ICalculatorService
    {
        /// <summary>
        /// 新增isoneway屬性表示, 這個函式是不是返回應答資訊 ; 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        [OperationContract(IsOneWay = true)]
        void Plus(int x, int y);


        //建立了一個Icalculator服務的協議
        //這個協議帶有一個回撥介面ICallBack
        //注意WCF的雙工, 不能全靠配置, 在建立服務的時候, 就要開始處理了
    }
}

  

c)         ICallBack程式碼如下

using System.ServiceModel;

namespace MyWCF.WCFSimpleDuplex.Interface
{
    /// <summary>
    /// 不需要協議, 因為它不是一個服務契約, 它只是一個約束,這個約束由客戶端實現
    /// </summary>
    public interface ICallBack
    {
        /// <summary>
        /// 這裡就是回撥的時候完成的函式, 這個介面也要加上Isoneway=true  
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n"></param>
        /// <param name="result"></param>
        [OperationContract(IsOneWay = true)]
        void Show(int m, int n, int result);
    }


    //要釋出一個雙工協議 ,首先在服務的建立WCF服務, 服務的契約上面指定callbackContract回撥的型別
    //建立回撥介面, 回撥介面就是等下要執行的介面
}

  

5. 接下來處理 MyWCF.WCFSimpleDuplex.Model 的程式碼, 它就一個類:

using System.Runtime.Serialization;

namespace MyWCF.WCFSimpleDuplex.Model
{
    [DataContract]
    public class WCFUser
    {
        //[DataMember]
        public int Id { get; set; }
        [DataMember]
        public int Age { get; set; }
        [DataMember]
        public int Sex { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Description { get; set; }
    }

    public enum WCFUserSex
    {
        Famale,
        Male,
        Other
    }
}

  

6. 關於服務類MyWCF.WCFSimpleDuplex.Service的處理如下:

using System.ServiceModel;
using MyWCF.WCFSimpleDuplex.Interface;

namespace MyWCF.WCFSimpleDuplex.Service
{
    /// <summary>
    /// 對ICalculatorService的一個實現類; 實現的時候, 除了要完成自己的計算, 還要完成, 對介面的回撥
    /// </summary>
    public class CalculatorService : ICalculatorService
    {
        /// <summary>
        /// 完成計算,然後去回撥
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void Plus(int x, int y)
        {
            int result = x + y;
            System.Threading.Thread.Sleep(3000);

            //OperationContext表示當前操作的上下文
            //OperationContext.Current.GetCallbackChannel<ICallBack>();這裡有點像IOC, 給你一個介面然後生成一個例項; 注意哦, 這裡的ICallBack還有沒有任何實現, 而這個實現是在客戶端(client)處去完成的
            ICallBack callBack =OperationContext.Current.GetCallbackChannel<ICallBack>();
            callBack.Show(x, y, result);
        }
    }
} 

7.       接下來是控制檯MyWCF.WCFSimpleDuplex.ConsoleTest的程式碼實現, 首先是他的配置檔案

a)         初始時的配置檔案如下圖: 

b)         在configuration節點新增如下配置:

 

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

<system.serviceModel>  <!--WCF的配置檔案 , WCF的配置檔案一個是基於TCP的, 一個是基於http的; 可以新增多個; 如果新增多個就是下面的配置格式 -->
  <behaviors> 
      <serviceBehaviors>
        <!--<behavior name="MathServicebehavior"> --><!--這裡配置第一個Behavior的節點--><!--
          <serviceDebug httpHelpPageEnabled="false"/>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceTimeouts transactionTimeout="00:10:00"/>
          <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
        </behavior>-->
        <behavior name="CalculatorServicebehavior">  <!--這裡配置第二個Behavior的節點-->
          <serviceDebug httpHelpPageEnabled="false"/>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceTimeouts transactionTimeout="00:10:00"/>
          <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
                               <!--在tcp中上面幾行和http的配置都是一樣的-->  
    <bindings>
      <netTcpBinding>
        <binding name="tcpbinding">   <!--指定為tcp的協議型別-->  
          <security mode="None">
            <!--clientCredentialType="None" → 表示加密型別  protectionLevel="None"  表示安全級別-->
            <transport clientCredentialType="None" protectionLevel="None"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
  
    <services> 
      <service name="MyWCF.WCFSimpleDuplex.Service.CalculatorService" behaviorConfiguration="CalculatorServicebehavior">
        <host> <!--這裡配置第一個服務的名稱和服務的具體指向類-->
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9999/CalculatorService"/><!--指定的協議為net tcp協議-->
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MyWCF.WCFSimpleDuplex.Interface.ICalculatorService"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>
      <!--<service name="MyWCF.WCFSimpleDuplex.Service.MathService" behaviorConfiguration="MathServicebehavior">
        <host>--><!--這裡配置第二個服務的名稱和服務的具體指向類--><!--
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9999/MathService"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MyWCF.WCFSimpleDuplex.Interface.IMathService"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>-->
    </services>
  </system.serviceModel>
</configuration>

c)         Program程式碼如下:

 

using System;
using System.ServiceModel;
using MyWCF.WCFSimpleDuplex.Service;

namespace MyWCF.WCFSimpleDuplex.ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("開始掛起服務");
                ServiceInit.Process();
            }
            catch (Exception ex)//如果報無法註冊. . ., 則說明需要管理員許可權, 啟動這個程式
            {
                //服務“SOA.WCF.Service.CalculatorService”有零個應用程式(非基礎結構)終結點。這可能是因為未找到應用程式的配置檔案,或者在配置檔案中未找到與服務名稱匹配的服務元素,或者服務元素中未定義終結點。
                //沒有配置檔案

                //另一應用程式已使用 HTTP.SYS 註冊了該 URL。
                //埠 9999 被其它應用程式佔用了, 找到並將其停止
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }


    /// <summary>
    /// WCF寄宿到控制檯
    /// </summary>
    public class ServiceInit
    {
        public static void Process()
        {
                       //ServiceHost →表示提供服務的主機,使用服務的型別及其指定的基址初始化 System.ServiceModel.ServiceHost 類的新例項。
             Type type = typeof(CalculatorService);
            ServiceHost host =  new ServiceHost(type);
            
            host.Opening += (s, e) => Console.WriteLine($"{type} 準備開啟");
            //已經開啟事件
            host.Opened += (s, e) => Console.WriteLine($"{type} 已經正常開啟");

            //開啟服務; 注意當這裡需要Open的時候, 會去配置檔案中檢查是否有配置了MathService這個類的behavior(行為)
            host.Open();

            Console.WriteLine("輸入任何字元,就停止");
            Console.Read();
            host.Close();
            Console.Read();
        }

    }
  
}

 

8. 完成以上步驟就可以啟動試試看了:

9. 可以使用  cmd 命令 netstat -ano | find "9999", 檢視下埠9999是否被監聽

 

10. 雙工的服務端已經處理完畢, 接下來開始處理客戶端, 當然客戶端首先就要對剛才的ICallBack進行實現, 這個毋容置疑的

11. 再新開啟一個VS2017, 建立一個控制檯測試專案:

 

12. 接下來不用多想, 第一件事, 新增服務引用, 第二件事實現服務端的那個ICallBack介面

a)         新增服務引用

 

b)         實現ICallBack介面, 注意這個類裡的Show 方法, 在客戶端沒有任何程式碼呼叫

 

using System;

namespace MyWCF.WCFSimpleDuplexConsoleTest.CTest
{
    /// <summary>
    /// 具體實現的回撥
    /// </summary>
    public class CallBackService : MyDuplexConsoleTest.ICalculatorServiceCallback
    {
        public void Show(int m, int n, int result)
        {
           
            Console.WriteLine($"回撥操作展示, 這個操作發生在兩秒之後:{m}+{n}={result}");
        }
    }
}

  

c)         整體client截圖:

 

13. 然後在program中的程式碼, 建議單步除錯看看程式碼的執行過程:

 

using System;
using System.ServiceModel;

namespace MyWCF.WCFSimpleDuplexConsoleTest.CTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MyDuplexConsoleTest.CalculatorServiceClient client = null;
            try
            {
                Console.WriteLine("客戶端來測試雙工!~~");

                //建立一個需要回調的例項, 等會服務端進行回撥的
                InstanceContext context = new InstanceContext(new CallBackService());


                client = new MyDuplexConsoleTest.CalculatorServiceClient(context);
                client.Plus(123, 234);
                client.Close();
            }
            catch (Exception ex)
            {
                if (client != null)
                    client.Abort();
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }
}

14. 結果截圖

  

15. 結束