20181102_WCF简单双工

Posted wxylog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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. 结束

以上是关于20181102_WCF简单双工的主要内容,如果未能解决你的问题,请参考以下文章

WCF 双工回调示例失败

WCF(Silverlight)双工 - 不打服务器

如何在 azure wcf 中继中处理双工 wcf

WCF 双工合同

WCF:是不是可以在双工通道中使用流模式?

WCF系列之双工通信