• 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. 结束

  • 相关阅读:
    Django学习之八:forms组件【对form舒心了】
    Django学习之七:Django 中间件
    Django学习之六:Django 常用模块导入记忆
    Django学习之五:Django 之 注意事项及汇总
    Django学习之四:Django Model模块
    工程师基本常识
    Redis详解
    Nginx浅析
    MySQL数据库引擎、事务隔离级别、锁
    浅谈HTTP中GET和POST请求方式的区别
  • 原文地址:https://www.cnblogs.com/wxylog/p/9895442.html
Copyright © 2020-2023  润新知