您的位置:首页 > 其它

[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)

2007-03-02 17:31 295 查看
昨天写了一篇Remoting中如何实现双向通信的文章《[原创].NET Remoting: 如何通过Remoting实现双向通信(Bidirectional Communication)
》,作为对比,今天我们来讨论一下WCF的双向通信。
为了使我们能够更好地对比双向通信在Remoting中和WCF中的实现,我们的Sample采用一样的业务逻辑——调用一个数学计算的远程调用,除了传递相应的操作数之外,我们还传递一个对象,这个对象可以在Server端中回调 (Callback) 把运算结果在Client端显示出来。

可以通过下面的URL下载源代码:
http://www.cnblogs.com/files/artech/Artech.WCFService.2007.03.02.zip
Step1:构建整个Solution的整体构架。



整个Solution的架构在我的之前的Blog有了详细的介绍([原创]我的WCF之旅(1):创建一个简单的WCF程序),这里只作一个简单的介绍。

Artech.WCFService.Contract: Class Library Project,用来保存Contract(Service Contact、Message Contract、Data Contract), 之所以把Contract独立出来的原因是考虑到他同时被Server端——Service本身和Service Hosting和Client端使用

 Artech.WCFService.Service:Class Library Project,Service的业务逻辑, 这个Project引用Artech.WCFService.Contract Project和System.ServiceModel DLL。

 Artech.WCFService.Hosting:Console Application, 用于以Self-Hosting的方式Host Service。这个Project引用Artech.WCFService.Contract和Artech. Project WCFService.Service。Project和System.ServiceModel DLL。

 Artech.WCFService.Client:Console Application, 用以模拟现实中的调用Service的Clinet。这个Project引用Artech.WCFService.Contract Project 和System.ServiceModel DLL。

 http://localhost/WCFService: Web Site Project, 用于模拟如何把Service Host到IIS中。这个Project引用Artech.WCFService.Contract、Artech.WCFService.Service和System.ServiceModel DLL。

Step 2 在Artech.WCFService.Contract定义Calculator Service 和Callback的Contract
1.IDuplexCalculator.cs



using System;


using System.Collections.Generic;


using System.Text;


using System.ServiceModel;




namespace Artech.WCFService.Contract






{


     [ServiceContract(CallbackContract = typeof(ICalculatorCallback))]


    public interface IDuplexCalculator




    

{


         [OperationContract]


        void Add(double x, double y);


    }


}


2.ICalculatorCallback.cs



using System;


using System.Collections.Generic;


using System.Text;


using System.ServiceModel;




namespace Artech.WCFService.Contract






{


    [ServiceContract]


    public interface ICalculatorCallback




    

{


        [OperationContract]


        void ShowResult(double x, double y, double result);


    }


}
这里有以下几点需要注意的:
 

1.在一个分布式的环境中,Client能够调用Service,它必须知道Service的Contract, Contract定义了Service暴露给外界的所有可用的Operation,以及这些Operation的签名(Signature).至于Service中定义的Opertion采用怎样的实现,Client不需要了解。这也是在WCF中把Service Contract与具体的Service Implementation相互分离的一个重要原因——我们把Contract单独提取出来,把他暴露给Client,从而可以避免把过多的暴露业务逻辑的实现。
2.在一个分布式的环境中,Serer端和Client并不是一成不变的,他们是可以相互转化的。提供服务的就是Server,消费Service的就是Client。在这个例子中,当Artech.WCFService.Client调用Host在Artech.WCFService.Hosting中的DuplexCalculatorService(定义在 Artech.WCFService.Service中),Artech.WCFService.Client是Client,而Server端的执行环境是Artech.WCFService.Hosting。而当Calculator Service回调(Callback)Client的逻辑把运算结果显示出来时候,因为Callback的逻辑是在Artech.WCFService.Client中执行的,所以Artech.WCFService.Client成了Server,而CalculatorCallbackHandler(定义在 Artech.WCFService.Client中)成了真正的Service。
3.我们已经说过Client能够调用Service,它必须知道Service的Contract。所以DuplexCalculatorService能过Callback Artech.WCFService.Client,它也必须知道回调操作的Contract。WCF通过在ServiceContractAttribute中的CallbackContrac参数在制定。


 [ServiceContract(CallbackContract = typeof(ICalculatorCallback))]


    public interface IDuplexCalculator




    

{


         [OperationContract]


        void Add(double x, double y);


    }
Step 3 在Artech.WCFService.Service定义Duplex Calculator Service


[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]


    public class DuplexCalculatorService:IDuplexCalculator




    

{




        IDuplexCalculator Members#region IDuplexCalculator Members




        public void Add(double x, double y)




        

{


            double result = x + y;


            ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();


            callback.ShowResult(x, y, result);


        }




        #endregion


    }
这里有以下几点需要注意的:
1. 必须把并发模式ConcurrencyMode设置为ConcurencyMode. Reentrant 或者ConcurencyMode.Multiple。要弄清种种的原因,我们先来看看在本例中的具体的消息交互的情况(假设我们的调用Duplex Calculator Service 和回调都采用Request/Response的Message Excahnge Pattern,时间上一般这种情况我们应该采用One-way的ME):



首先Client调用Duplex Calculator Service, Service Request从Client到Service,Service开始执行运算,运算完成后Callback Client将运算结构在Client端显示出来,这个过程中Service向Client发送一个Callback Message,等Client完成Callback操作后,会向Service端发送一个Callback Response(实际上是一个空的Message——以为Callback操作没有返回值),Service收到Callback Response之后,会执行后续的操作,等所有的操作执行完毕,会发送Service Response(这里也是一个空的Message)到Client。
现在我们 来看看为什么在建立Duplex Service的时候要把并发模式设为ConcurencyMode. Reentrant 或者ConcurencyMode.Multiple。在默认的并发模式下(ConcurencyMode.Single),WCF为了保证其线程安全性(Thread Safety),在整个调用Service的过程中,InstanceContext会被WCF锁住(Lock)。一本Sample为例,从Client向Service发送Service Request 到手的Server发回的Service Resqonse,整个InstanceContext会在Server端被锁住, 由于在Client执行的Callback操作使用的是同一个InstanceContext, 这样形成了一个死锁(DeadLock)——Calculator Service必须等Callback操作完成之后才能执行后续的操作,而Callback操作必须等待InstanceContext被解锁(Unlock)才能执行,然而InstanceContext却被Calculator Service锁住。
当ConcurencyMode为ConcurencyMode. Reentrant 或者ConcurencyMode.Multiple的时候。当Serivice向外调用某个操作(outgoing call)的时候,或者说在向外发送一个Outgoing Message的时候,WCF会解锁(Unlock)InstanceContext。以本例为例,Service 回调Client的时候,原来被锁住的InstanceContext会被解锁。这样Callback操作就可以利用InstanceContext来执行。
2. Service可以通过OperationContext.Current.GetCallbackChannel<T>() 来或者Client在调用Calculator Service时指定的Callback Object。其中T一般被指定为Callback Contract对应的Type。 


ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
Step 4 在Artech.WCFService.Hosting中Host Duplex  Calculator  Service


<?xml version="1.0" encoding="utf-8" ?>


<configuration>  


    <system.serviceModel>


        <behaviors>


            <serviceBehaviors>


                <behavior name="calculatorServieBehavior">


                    <serviceMetadata httpGetEnabled="true" />


                </behavior>


            </serviceBehaviors>


        </behaviors>      


        <services>            


            <service behaviorConfiguration="calculatorServieBehavior" name="Artech.WCFService.Service.DuplexCalculatorService">


                <endpoint binding="wsDualHttpBinding" contract="Artech.WCFService.Contract.IDuplexCalculator">                  


                </endpoint>


                <host>


                    <baseAddresses>


                        <add baseAddress="http://localhost:7777/DuplexCalculator" />


                    </baseAddresses>


                </host>


            </service>            


        </services>


    </system.serviceModel>


</configuration>



Program.cs 


using System;


using System.Collections.Generic;


using System.Text;


using System.ServiceModel;


using Artech.WCFService.Contract;


using Artech.WCFService.Service;


using System.ServiceModel.Description;




namespace Artech.WCFService.Hosting






{


    class Program




    

{


        static void Main(string[] args)




        

{


            HostDuplexCalculator();


        }




       static void HostDuplexCalculator()




        

{


            using (ServiceHost calculatorSerivceHost = new ServiceHost(typeof(DuplexCalculatorService)))




            

{


                calculatorSerivceHost.Opened += delegate




                

{


                    Console.WriteLine("Duplex calculator Service has begun to listen 

 

");


                };




                calculatorSerivceHost.Open();




                Console.Read();


            }


        }        


    }


}



这里需要注意的时候,在Host Duplex  Calculator Service 的时候,我们要为它添加相应的Endpoint。对于支持双向通信的Service,它对Endpoint有一定的要求——我们必须为它指定一个支持Duplex MEP(Message Exchange Pattern)的Binding——比如wsDualHttpBinding,netDualTcpBinding。这里我们使用的时wsDualHttpBinding。
Step 5 在Artech.WCFService.Client定义Callback对象和调用Duplex  Calculator  Service 




DuplexCalculatorClient.cs


using System;


using System.Collections.Generic;


using System.Text;


using System.ServiceModel;




using Artech.WCFService.Contract;




namespace Artech.WCFService.Client






{


    class DuplexCalculatorClient:ClientBase<IDuplexCalculator>,IDuplexCalculator




    

{




        public DuplexCalculatorClient(InstanceContext callbackInstance)


            : base(callbackInstance)




        

{ }






        IDuplexCalculator Members#region IDuplexCalculator Members




        public void Add(double x, double y)




        

{


            this.Channel.Add(x, y);


        }




        #endregion


    }


}



App.config


<?xml version="1.0" encoding="utf-8" ?>


<configuration>


  <system.serviceModel>


    <bindings>


      <wsDualHttpBinding>


          <binding name="wsDualBinding_IDuplexCalculator" clientBaseAddress="http://localhost:6666/myClient/" />


      </wsDualHttpBinding>


    </bindings>


    <client>      


      <endpoint address="http://localhost:7777/DuplexCalculator" binding="wsDualHttpBinding"


        bindingConfiguration="wsDualBinding_IDuplexCalculator" contract="Artech.WCFService.Contract.IDuplexCalculator"


        name="duplexCalculatorEndpoint" />


      <endpoint address="http://localhost/WCFService/SessionfulCalculatorService.svc"


        binding="wsHttpBinding" bindingConfiguration="" contract="Artech.WCFService.Contract.ISessionfulCalculator" />


    </client>


  </system.serviceModel>


 </configuration>



CalculatorCallbackHandler.cs




using System;


using System.Collections.Generic;


using System.Text;


using Artech.WCFService.Contract;




namespace Artech.WCFService.Client






{


    class CalculatorCallbackHandler:ICalculatorCallback




    

{




        ICalculatorCallback Members#region ICalculatorCallback Members




        public void ShowResult(double x, double y, double result)




        

{


            Console.WriteLine("x + y = {2} where x = {0} and y = {1}", x, y, result);


        }




        #endregion


    }


}
Program.cs


using System;


using System.Collections.Generic;


using System.Text;


using System.ServiceModel;


using System.ServiceModel.Channels;




using Artech.WCFService.Contract;




namespace Artech.WCFService.Client






{


    class Program




    

{


        static void Main()




        

{


            try




            

{                   InvocateDuplexCalculator();


            }


            catch (Exception ex)




            

{


                Console.WriteLine(ex.Message);


            }




            Console.Read();    


        }




        


        static void InvocateDuplexCalculator()




        




            CalculatorCallbackHandler callbackHandler = new CalculatorCallbackHandler();




            using(DuplexCalculatorClient calculator = new DuplexCalculatorClient(new InstanceContext(callbackHandler)))




            

{


                Console.WriteLine("Begin to invocate duplex calculator

 

");


                calculator.Add(1, 2);


            }


        }        


    }


}



这里有以下几点需要注意的:
1. 在调用Duplex Calculator Service的时候,我们需要指定执行回调的Callback对象。在WCF中,Callback对象用一个InstanceContext对象来表示。而他在DuplexCalculatorClient的构造函数中指定。


 public DuplexCalculatorClient(InstanceContext callbackInstance)


            : base(callbackInstance)




        

{ }
2. Client调用Duplex Calculator Service,Service端需要注册相应的Channel来监听来自Client的请求。同理,Service回调Client,Client也需要相应的Channel来监听来自Service的回调。这个Channel通过下面的方式注册。   


    


<wsDualHttpBinding>


          <binding name="wsDualBinding_IDuplexCalculator" clientBaseAddress="http://localhost:6666/myClient/" />


</wsDualHttpBinding>

到现在为止我们已经完成了所有的Program,我们来运行一下。
1.         运行Artech.DuplexRemoting.Hosting



2.         运行Artech. WCFService.Client



将Duplex Calculator  Service Host 到IIS中
1.         在http://localhost/WCFService中添加于Artech.WCFService.Service。DuplexCalculatorService相对应的SVC文件。
DuplexCalculatorService.svc


<%@ ServiceHost Language="C#" Debug="true" Service="Artech.WCFService.Service.DuplexCalculatorService" %>
2.         并添加类似于Artech.WCFService.Hosting/App.Config中 的Configuration。


<?xml version="1.0"?>


<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">


  <system.serviceModel>


    <behaviors>


      <serviceBehaviors>


        <behavior name="calculatorServiceBehavior">


          <serviceMetadata httpGetEnabled="true" ></serviceMetadata>


        </behavior>


      </serviceBehaviors>


    </behaviors>


    <services>


      <service behaviorConfiguration="calculatorServiceBehavior" name="Artech.WCFService.Service.DuplexCalculatorService">


        <endpoint binding="wsDualHttpBinding" contract="Artech.WCFService.Contract.IDuplexCalculator" />


      </service>      


    </services>


  </system.serviceModel>  


    <system.web>


        <compilation debug="true">


            <assemblies>


                <add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>


                <add assembly="Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>


                <add assembly="SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>


                <add assembly="System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>


                <add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>


                <add assembly="System.Web.RegularExpressions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>


                <add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>


                <add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>


                <add assembly="System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/></assemblies></compilation>


    </system.web>


</configuration>



这样我们可以不需要Hosting的情况下通过这样的Uri访问Duplex Calculator Service:http://localhost/Artech.WCFService/ DuplexCalculatorService.svc。 
3.         修改Client的App.Config——修正Endpoint 的Address:


<?xml version="1.0" encoding="utf-8" ?>


<configuration>


  <system.serviceModel>


    <bindings>


      <wsDualHttpBinding>


          <binding name="wsDualBinding_IDuplexCalculator" clientBaseAddress="http://localhost:6666/myClient/" />


      </wsDualHttpBinding>


    </bindings>


    <client>      


      <endpoint address=" http://localhost/Artech.WCFService/ DuplexCalculatorService.svc " binding="wsDualHttpBinding"


        bindingConfiguration="wsDualBinding_IDuplexCalculator" contract="Artech.WCFService.Contract.IDuplexCalculator"


        name="duplexCalculatorEndpoint" />


      <endpoint address="http://localhost/WCFService/SessionfulCalculatorService.svc"


        binding="wsHttpBinding" bindingConfiguration="" contract="Artech.WCFService.Contract.ISessionfulCalculator" />


    </client>


  </system.serviceModel>


 </configuration>
在不起用Hosting的情况下运行Artech.WCFService.Client,我们一样可以得到相同的结果。

WCF相关内容:
[原创]我的WCF之旅(1):创建一个简单的WCF程序
[原创]我的WCF之旅(2):Endpoint Overview
[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional
Communication)
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part I
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part II
[原创]我的WCF之旅(5):Service Contract中的重载(Overloading)
[原创]我的WCF之旅(6):在Winform Application中调用Duplex
Service出现TimeoutException的原因和解决方案
[原创]我的WCF之旅(7):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service
Contract的继承
[原创]我的WCF之旅(8):WCF中的Session和Instancing Management

[原创]我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
[原创]我的WCF之旅(10): 如何在WCF进行Exception Handling
[原创]我的WCF之旅(11):再谈WCF的双向通讯-基于Http的双向通讯 V.S.
基于TCP的双向通讯
[原创]我的WCF之旅(12):使用MSMQ进行Reliable Messaging
[原创]我的WCF之旅(13):创建基于MSMQ的Responsive Service
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐