zoukankan      html  css  js  c++  java
  • WCF服务通信测试

    知识需要反复咀嚼,常读常新,简单的WCF通信测试:basicHttpBinding(基本通信) etTcpBinding(双工通信) etMsmqBinding(消息队列),简单的测试Demo。
    简单说一下代码结构,后续整理一下具体的实现逻辑,为什么这么处理。

    1.WCFTest.DataContracts类库代码(基础数据契约类库)
    <1>.OrderItem.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    using System.Threading.Tasks;
    
    namespace WCFTest.DataContracts
    {
        [DataContract]
        public class OrderItem
        {
            [DataMember]
            public Guid ProductID;
    
            [DataMember]
            public string ProductName;
    
            [DataMember]
            public decimal UnitPrice;
    
            [DataMember]
            public int Quantity;
        }
    }
    View Code

    <2>.Order.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    using System.Threading.Tasks;
    
    namespace WCFTest.DataContracts
    {
        [DataContract]
        [KnownType(typeof(OrderItem))]
        public class Order
        {
            [DataMember]
            public Guid OrderNo;
    
            [DataMember]
            public DateTime OrderDate;
    
            [DataMember]
            public Guid SupplierID;
    
            [DataMember]
            public string SupplierName;
    
            [DataMember]
            public List<OrderItem> OrderItems=new List<OrderItem> ();
            //如果不这样的构建方式,使用请使用get;set;,并且Order构造函数实例化(new)这个集合
    
            /// <summary>
            /// 订单信息描述
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                StringBuilder strBuilder = new StringBuilder();
    
                strBuilder.AppendLine("General Information:
    	");
                strBuilder.AppendLine(string.Format("Order No:{0}", OrderNo));
                strBuilder.AppendLine(string.Format("Order Date:{0}", OrderDate.ToShortDateString()));
                strBuilder.AppendLine(string.Format("SupplierID:{0}", SupplierID));
                strBuilder.AppendLine(string.Format("SupplierName:{0}", SupplierName));
    
                strBuilder.AppendLine("
    Products:");
                foreach (OrderItem order in OrderItems)
                {
                    strBuilder.AppendLine(string.Format("ProductID:{0}", order.ProductID));
                    strBuilder.AppendLine(string.Format("ProductName:{0}", order.ProductName));
                    strBuilder.AppendLine(string.Format("UnitPrice:{0}", order.UnitPrice));
                    strBuilder.AppendLine(string.Format("Quantity:{0}", order.Quantity));
                }
                return strBuilder.ToString();
            }
        }
    }
    View Code

    2.WCFTest.Contracts类库代码(服务端和客户端通信契约接口)
    <1>.ICalculator.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Threading.Tasks;
    
    namespace WCFTest.Contracts
    {
    
        [ServiceContract]
        public interface ICalculator
        {
            [OperationContract]
            double Add(double numA, double numB);
    
            [OperationContract]
            double Sub(double numA, double numB);
    
            [OperationContract]
            double Multiply(double numA, double numB);
    
            [OperationContract]
            double Divide(double numA, double numB);
        }
    }
    View Code

    <2>.IDuplexCallBack.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Threading.Tasks;
    
    namespace WCFTest.Contracts
    {
        public interface IDuplexCallBack
        {
            [OperationContract(IsOneWay = true)]
            void DisplayResult(double result);
        }
    }
    View Code

    <3>.IDuplexContract.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.Threading.Tasks;
    
    namespace WCFTest.Contracts
    {
        #region BaseInfoKnown
        /*
         * 如何理解双工通信?
         * 请求过程中的回调,双工消息交换模式的表现形式,客户端调用服务的时候,附加上一个回调对象;
         * 服务端在处理该请求中,通过客户端附加的回调对象(调用回调服务的代理对象)回调客户端操作(该操作在客户端执行)。
         * 整个消息交换过程由两个基本的消息交换,其一客户端正常的服务请求,其二服务端对客户端的回调。两者可以采用请求-回复模式,也可以采用单向(One-way)的MEP进行消息交换。
         * 
         * 如何模拟测试?
         * 本例采用另外一种截然不同的方式调用服务并进行结果的输出:
         * 通过单向(One-way)的模式调用CalculuateService(也就是客户端不可能通过回复消息得到计算结果),服务端在完成运算结果后,通过回调(Callback)的方式在客户端将计算结果打印出来。
         */
        #endregion
    
        [ServiceContract(CallbackContract = typeof(IDuplexCallBack))]
        public interface IDuplexContract
        {
            [OperationContract(IsOneWay = true)]//单向(只是客户端请求,未做响应[回复])
            void Add(double numA, double numB);
        }
    }
    View Code

    <4>.IMSMQContract.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.DataContracts;
    using System.Threading.Tasks;
    
    namespace WCFTest.Contracts
    {
        #region BaseInfoKnown
        /*
         * 比如,在Intranet内,通过TCP进行高效的数据通信;而在Internet内,通常使用Http进行跨平台数据交换。
         * 这些通信的特点都是基于Connection的,也就是说,交互双方必须有一个可用的Connection存在于他们之间。
         * 而在某些时候比如用户拨号、便携式计算机的用户,不能保证需要Server时有一个可用的Connection。这时候,基于Messaging Queue尤为重要。
         * MSMQ的好处:
         * <1>.MSMQ是基于Disconnection。这种通信方式为离线工作成为了可能。
         * <2>.MSMQ天生是One-way、异步的。对于用户的请求,Server端无需立即相应,也就是说,Server对数据的处理无需和Client的数据发送进行同步。这样可以避免峰值负载。
         * <3>.MSMQ能够提供高质量的Reliable Messaging。异步通信,无法获知Message是否抵达Server端,也无法获知Server端的执行结果和出错信息。
         * MSMQ提供Reliable Messaging的机制:
         * a.超时机制(Timeout)
         * b.确认机制(Acknowledgement)
         * c.日志机制(Journaling):消息被发送或接收后,被Copy一份。
         * d.死信队列(Dead letter Queue):保存发送失败的message。 
         * 
         * 在WCF中,MSMQ的数据传输功能被封装在一个Binding中,提供WCF Endpoint之间、以及Endpoint和现有的基于MSMQ的Application进行通信的实现。
         * 提供两种不同的built-in binding:
         * <1>.NetMsmqBinding:从提供的功能和使用方式上看,和一般的使用的binding一样,所不同的是,它提供的是基于MSMQ的Reliable Messaging。变成模式和一般的binding完全一样。
         * <2>.MsmqIntergrationBinding:主要用于将我们的WCF Application和现有的基于MSMQ的Application集成的情况。
         * 实现了WCF Endpoint和某个Message Queue进行数据的通信,具体来说,就是实现了单一的向某个Message Queue发送Message,和从某个Message Queue中接收Message的功能。
         * 
         * MSMQ则有效地提供了这样的机制:Server端建立一个Message Queue来接收来个客户的订单,客户端通过向该Message Queue发送承载了订单数据的Message实现订单的递交。
         * 如果客户在离线的情况,仍然可以通过客户端进行订单递交的操作,存储着订单数据的Message会被暂保在本地的Message Queue中,一旦客户联机,MSMQ将Message从中取出,发送到真正的接收方,这个动作对于用户的透明的。
         */
        #endregion
    
        [ServiceContract]
        [ServiceKnownType(typeof(Order))]
        public interface IMSMQContract
        {
            [OperationContract(IsOneWay = true)]
            void SubmitOrder(Order order);
        }
    }
    View Code

    3.WCFTest.Services类库代码(契约接口业务功能实现)
    <1>.CalculatorService.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Contracts;
    using System.Threading.Tasks;
    
    namespace WCFTest.Services
    {
        public class CalculatorService : ICalculator
        {
            public double Add(double numA, double numB)
            {
                return numA + numB;
            }
    
            public double Sub(double numA, double numB)
            {
                return numA - numB;
            }
    
            public double Multiply(double numA, double numB)
            {
                return numA * numB;
            }
    
            public double Divide(double numA, double numB)
            {
                if (numB != 0)
                {
                    return numA / numB;
                }
                return 0;
            }
        }
    }
    View Code

    <2>.DuplexService.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Contracts;
    using System.Threading.Tasks;
    
    namespace WCFTest.Services
    {
        /// <summary>
        /// 在Server端,通过OperationContext.Current.GetCallbackChannel<T>()
        /// 获得Client指定的CallbackContent Instance,进行Client调用Opertion
        /// </summary>
        public class DuplexService : IDuplexContract
        {
            public void Add(double numA, double numB)
            {
                double result = numA + numB;
                IDuplexCallBack duplexCall = OperationContext.Current.GetCallbackChannel<IDuplexCallBack>();
                duplexCall.DisplayResult(result);
            }
        }
    }
    View Code

    <3>.MSMQService.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Contracts;
    using WCFTest.DataContracts;
    using System.Threading.Tasks;
    
    namespace WCFTest.Services
    {
        public class MSMQService:IMSMQContract
        {
            //设定服务端请求行为:需求事务操作自动完成事务
            [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)]
            public void SubmitOrder(WCFTest.DataContracts.Order order)
            {
                //将Order数据保存到数据库
                SaveData.SaveOrder(order);
    
                Console.WriteLine("Receive An Order:");
                Console.WriteLine(order.ToString()); //服务端输出的信息,在Host监听器可以看到,服务是不可见的
            }
        }
    }
    View Code

    <4>.SaveData.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using WCFTest.DataContracts;
    using System.Threading.Tasks;
    
    namespace WCFTest.Services
    {
        public class SaveData
        {
            private static Dictionary<Guid, Order> dicOrder = new Dictionary<Guid, Order>();
    
            public static void SaveOrder(Order order)
            {
                if (!dicOrder.Keys.Contains(order.OrderNo))
                {
                    dicOrder.Add(order.OrderNo, order);
                }
            }
    
            public static Order GetOrder(Guid orderNo)
            {
                Order order = new Order();
                if (dicOrder.Keys.Contains(orderNo))
                {
                    order=dicOrder[orderNo];
                }
                return order;
            }
        }
    }
    View Code

    4.WCFTest.Hots 控制台程序代码(多服务宿主程序)
    <1>.App.config (服务端和客户端通信配置ABC)

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        </startup>
      <system.serviceModel>
        <!--配置行为-->
        <behaviors>
          <!--配置服务端行为-->
          <serviceBehaviors>
            <behavior name="CalculatorBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:7070/CalculatorService/Metadata"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <!--配置协议-->
        <bindings>
          <!--MSMQBinding配置-->
          <netMsmqBinding>
            <binding name="MSMQBinding" exactlyOnce="true">
              <security mode="None">
                <transport msmqAuthenticationMode="None" msmqProtectionLevel="None"/>
                <message clientCredentialType="None"/>
              </security>
            </binding>
          </netMsmqBinding>
        </bindings>    
        <services>
          <!--CalculatorService-->
          <service name="WCFTest.Services.CalculatorService">
            <endpoint address="http://localhost:7070/CalculatorService"
                      binding="basicHttpBinding"
                      contract="WCFTest.Contracts.ICalculator">
            </endpoint>
          </service>
          <!--DuplexService-->
          <service name="WCFTest.Services.DuplexService">
            <endpoint address="net.tcp://localhost:6060/DuplexService"
                      binding="netTcpBinding"
                      contract="WCFTest.Contracts.IDuplexContract">
            </endpoint>
          </service>
          <!--MSMQService-->
          <service  name="WCFTest.Services.MSMQService">
            <endpoint address="net.msmq://localhost/Private/Orders"  
                      binding="netMsmqBinding"
                      bindingConfiguration="MSMQBinding"                     
                      contract="WCFTest.Contracts.IMSMQContract">
            </endpoint>
          </service>
        </services>
      </system.serviceModel>
    </configuration>

    <2>.Program.cs  (多服务宿主启用监听)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Services;
    using System.Configuration;
    using System.Threading.Tasks;
    using System.Xml;
    using System.Configuration;
    using System.Messaging;
    
    namespace WCFTest.Hots
    {
        /// <summary>
        /// 如何可以按需开启不同的Host
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                #region 单Host调用测试
                ////计算服务Host
                //CalculatorHosts();
                ////双工通信服务Host
                //DuplexHost();
                #endregion
    
                #region 多Host调用测试
                Dictionary<string, ServiceHost> dicHosts = new Dictionary<string, ServiceHost>();
                dicHosts.Add("CalculatorHost", new ServiceHost(typeof(Services.CalculatorService)));
                dicHosts.Add("DuplexHost", new ServiceHost(typeof(Services.DuplexService)));
                dicHosts.Add("MSMQHost", new ServiceHost(typeof(Services.MSMQService)));
    
    
                foreach (KeyValuePair<string, ServiceHost> keyPair in dicHosts)
                {
                    if (keyPair.Key.Contains("MSMQHost"))//MSMQHost的附加逻辑
                    {
                        //MSMQ队列不存在,或您没有足够的权限执行该操作异常:因为是这里的路径HostClient的地址路径问题
                        string strMSMQPath = @".Private$Orders";
                        //string strMSMQPath = ConfigurationManager.AppSettings["MSMQPath"];
                        if (!MessageQueue.Exists(strMSMQPath))
                        {
                            //创建一个MessageQueue,参数说明:MSMQ的位置,支持事务队列
                            MessageQueue.Create(strMSMQPath,true);
                        }
                    }
    
                    keyPair.Value.Opened += delegate
                    {
                        Console.WriteLine(keyPair.Key + " Host Has Been Listening.......");
                    };
                    keyPair.Value.Open();
                }
    
                Console.WriteLine("
    Please Enter Any Key To Abort All Hosts:");
                Console.ReadLine();
                foreach (KeyValuePair<string, ServiceHost> keyPair in dicHosts)
                {
                    keyPair.Value.Abort();
                    keyPair.Value.Close();
                    Console.WriteLine(keyPair.Key + " Host Has Been Stoped!");
                }
                #endregion
    
                Console.WriteLine("
    Please Enter Any Key Exit:");
                Console.Read();
            }
    
            /// <summary>
            /// 计算服务Host
            /// </summary>
            private static void CalculatorHosts()
            {
                using (ServiceHost host = new ServiceHost(typeof(Services.CalculatorService)))
                {
                    host.Opened += delegate
                    {
                        Console.WriteLine("Host Has Been Opened! Please Enter Any Key To Abort This Host:");
                        Console.ReadKey();
                        host.Abort();
                        host.Close();
                        Console.WriteLine("Host Has Been Stoped!");
                    };
                    host.Open();
                }
            }
    
            /// <summary>
            /// 双工通信服务Host
            /// </summary>
            private static void DuplexHost()
            {
                using (ServiceHost duplexHost = new ServiceHost(typeof(Services.DuplexService)))
                {
                    duplexHost.Opened += delegate
                    {
                        Console.WriteLine("Duplex Host Has Been Listening....! Please Enter Any Key To Abort This Host:");
                        Console.ReadLine();
                        duplexHost.Abort();
                        duplexHost.Close();
                        Console.WriteLine("Duplex Host Has Been Stoped!");
                    };
                    duplexHost.Open();
                }
            }
        }
    }

    5.WCFTravelReview 控制台程序代码 (客户端调用)
    <1>.App.config (客户端和服务端通信配置ABC)

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        </startup>
      <system.serviceModel>
        <bindings>
          <netMsmqBinding>
            <binding name="MSMQBinding" exactlyOnce="true">
              <security mode="None">
                <transport msmqAuthenticationMode="None" msmqProtectionLevel="None"/>
                <message clientCredentialType="None"/>
              </security>
            </binding>
          </netMsmqBinding>
        </bindings>
        
        <client >
          <!--CalculatorService终结点-->
          <endpoint name="CalculatorClient"
                    address="http://localhost:7070/CalculatorService"
                    binding="basicHttpBinding"
                    contract="WCFTest.Contracts.ICalculator"></endpoint>
          <!--DuplexService终结点-->
          <endpoint name="DuplexClient"
                    address="net.tcp://localhost:6060/DuplexService"
                    binding="netTcpBinding"
                    contract="WCFTest.Contracts.IDuplexContract"></endpoint>
          <!--MSMQService终结点-->
          <endpoint name="MSMQClient" 
                    address="net.msmq://loclahost/Private/Orders"
                    binding="netMsmqBinding"
                    bindingConfiguration="MSMQBinding"
                    contract="WCFTest.Contracts.IMSMQContract"></endpoint>
        </client>
      </system.serviceModel>
    </configuration>

    <2>.DuplexCallBackClient.cs (双工通信回调接口实现)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Contracts;
    using System.Threading.Tasks;
    
    namespace WCFTravelReview
    {
        //客户端的回调类,显示计算结果,实现ICallback接口
        public class DuplexCallBackClient : IDuplexCallBack
        {
            public void DisplayResult(double result)
            {
                Console.WriteLine("The Result is {0}", result.ToString());
            }
        }
    }

    <3>.Program.cs (测试客户端调用)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using WCFTest.Contracts;
    using WCFTest.DataContracts;
    using System.Transactions;
    using System.Threading.Tasks;
    
    namespace WCFTravelReview
    {
        class Program
        {
            static void Main(string[] args)
            {
                //计算服务Client
                CalculatorClient();
    
                //双工服务Client
                DuplexClient();
    
                //消息队列
                MSMQClient();
    
                Console.Read();
            }
    
            /// <summary>
            /// 计算服务Client
            /// </summary>
            private static void CalculatorClient()
            {
                using (ChannelFactory<ICalculator> calculatorChannel = new ChannelFactory<ICalculator>("CalculatorClient"))
                {
                    ICalculator proxy = calculatorChannel.CreateChannel();
                    using (proxy as IDisposable)
                    {
                        Console.WriteLine("计算通信测试:");
                        Console.WriteLine("请输入数字A:");
                        double numA = Convert.ToDouble(Console.ReadLine());
                        Console.WriteLine("请输入数字B:");
                        double numB = Convert.ToDouble(Console.ReadLine());
    
                        Console.WriteLine("WCF调用服务计算:");
                        Console.WriteLine("{0}+{1}={2}", numA, numB, proxy.Add(numA, numB));
                        Console.WriteLine("{0}-{1}={2}", numA, numB, proxy.Sub(numA, numB));
                        Console.WriteLine("{0}*{1}={2}", numA, numB, proxy.Multiply(numA, numB));
                        Console.WriteLine("{0}/{1}={2}", numA, numB, proxy.Divide(numA, numB));
                    }
                }
            }
    
            /// <summary>
            /// 双工服务Client
            /// </summary>
            private static void DuplexClient()
            {
                #region TCP/IP DuplexInfo Known
                /*
                 * 对于双工通信
                 * 对于TCP/IP簇中的传输层协议TCP,它则是一个基于Connection的协议,在正式进行数据传输之前,
                 * 必须要在Client和Server之后建立一个Connection,Connection的建立通过经典的“3次握手”。
                 * TCP具有Duplex的特性,就是说Connection被创建之后,从Client到Server,从Server到Client的数据传递可以利用同一个Connection来实现。
                 * 对于WCF的双向通信,Client调用Service,Service Callback Client使用的都是同一个Channel。
                 */
                #endregion
    
                InstanceContext instanceContext = new InstanceContext(new DuplexCallBackClient());
                using (DuplexChannelFactory<IDuplexContract> duplexChannel = new DuplexChannelFactory<IDuplexContract>(instanceContext, "DuplexClient"))
                {
                    IDuplexContract proxy = duplexChannel.CreateChannel();
                    using (proxy as IDisposable)
                    {
                        Console.WriteLine("
    双工通信测试:");
                        Console.WriteLine("请输入数字A:");
                        double numA = Convert.ToDouble(Console.ReadLine());
                        Console.WriteLine("请输入数字B:");
                        double numB = Convert.ToDouble(Console.ReadLine());
                        Console.WriteLine("WCF调用双工通信计算:");
                        proxy.Add(numA, numB);
                    }
                }
            }
    
            /// <summary>
            /// MSMQ订单Client
            /// </summary>
            private static void MSMQClient()
            {
                //创建订单数据
                Order order = new Order() { OrderNo=Guid.NewGuid(), OrderDate=DateTime.Now, SupplierID=Guid.NewGuid(), SupplierName="SupplierName" };
                OrderItem orderItem = new OrderItem() { ProductID=Guid.NewGuid(), ProductName="PP", Quantity=1, UnitPrice=1 };
    
                order.OrderItems.Add(new OrderItem() { ProductID=Guid.NewGuid(), ProductName="ProductName1", Quantity=300, UnitPrice=10 });
                order.OrderItems.Add(new OrderItem() { ProductID = Guid.NewGuid(), ProductName = "ProductName2", Quantity = 10, UnitPrice = 1 });
    
                ChannelFactory<IMSMQContract> msmqChannel = new ChannelFactory<IMSMQContract>("MSMQClient");
                IMSMQContract proxy = msmqChannel.CreateChannel();
                using (proxy as IDisposable)
                {
                    Console.WriteLine("
    消息队列通信测试:");
                    Console.WriteLine("MSMQ Submit Order To Server....");
                    using (TransactionScope tansactionScope = new TransactionScope(TransactionScopeOption.Required))
                    {
                        proxy.SubmitOrder(order);
                        tansactionScope.Complete();
                        Console.WriteLine("Order Has Been Submit! Complete This Order!");
                    }
                }
    
            }
        }
    }

     6.运行效果:

  • 相关阅读:
    时间序列数据库(TSDB)初识与选择(InfluxDB、OpenTSDB、Druid、Elasticsearch对比)
    Prometheus COMPARISON TO ALTERNATIVES
    认真分析mmap:是什么 为什么 怎么用
    Flume学习之路 (二)Flume的Source类型
    Flume学习之路 (一)Flume的基础介绍
    Spark学习之路 (二十一)SparkSQL的开窗函数和DataSet
    Spark学习之路 (二十)SparkSQL的元数据
    CentOS 7的安装
    Spark学习之路 (十九)SparkSQL的自定义函数UDF
    Spark学习之路 (十八)SparkSQL简单使用
  • 原文地址:https://www.cnblogs.com/SanMaoSpace/p/3722097.html
Copyright © 2011-2022 走看看