首页 > 代码库 > WCF服务通信测试

WCF服务通信测试

知识需要反复咀嚼,常读常新,简单的WCF通信测试:basicHttpBinding(基本通信)\netTcpBinding(双工通信)\netMsmqBinding(消息队列),简单的测试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:\n\t");
            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("\nProducts:");
            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队列不存在,或您没有足够的权限执行该操作异常:因为是这里的路径\Host\Client的地址路径问题
                    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("\nPlease 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("\nPlease 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("\n双工通信测试:");
                    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("\n消息队列通信测试:");
                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.运行效果: