Duplex Mode Message Exchange Model
Duplex pattern is one of the ways WCF Exchanges Message between Client and Server. by the way. the other two is one-way and request-reply. in somehow, Duplex pattern maybe base on the others . like msdn says:
duplex pattern is characterized by the ability of both the service and the client to send messages to each other independently whether using one-way or request/reply messaging. This form of two-way communication is useful for services that must communicate directly to the client or for providing an asynchronous experience to either side of a message exchange, including event-like behavior.
The duplex pattern is slightly more complex than the request/reply or one-way patterns because of the additional mechanism for communicating with the client.
To design a duplex contract, you must also design a callback contract and assign the type of that callback contract to the CallbackContract property of the ServiceContractAttribute attribute that marks your service contract.
To implement a duplex pattern, you must create a second interface that contains the method declarations that are called on the client.
For an example of creating a service, and a client that accesses that service, see How to: Create a Duplex Contract and How to: Access Services with a Duplex Contract. For a working sample, see Duplex. For more information about issues using duplex contracts, see Duplex Services.
How to: Create a Duplex Contract
This topic shows the basic steps to create methods that use a duplex (two-way) contract. A duplex contract allows clients and servers to communicate with each other independently so that either can initiate calls to the other. The duplex contract is one of three message patterns available to Windows Communication Foundation (WCF) services. The other two message patterns are one-way and request-reply. A duplex contract consists of two one-way contracts between the client and the server and does not require that the method calls be correlated. Use this kind of contract when your service must query the client for more information or explicitly raise events on the client. For more information about creating a client application for a duplex contract, see How to: Access Services with a Duplex Contract. For a working sample, see the Duplex sample.
//The service uses the PerSession instance mode to maintain the result for each session.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required, CallbackContract=typeof(ICalculatorDuplexCallback))] public interface ICalculatorDuplex { [OperationContract(IsOneWay=true)] void Clear(); [OperationContract(IsOneWay = true)] void AddTo(double n); [OperationContract(IsOneWay = true)] void SubtractFrom(double n); [OperationContract(IsOneWay = true)] void MultiplyBy(double n); [OperationContract(IsOneWay = true)] void DivideBy(double n); }
Create the callback interface that defines the set of operations that the service can invoke on the client.
public interface ICalculatorDuplexCallback { [OperationContract(IsOneWay = true)] void Equals(double result); [OperationContract(IsOneWay = true)] void Equation(string eqn); }
Implement the ServiceContract Interface:
// Service class which implements a duplex service contract. // Use an InstanceContextMode of PerSession to store the result // An instance of the service will be bound to each duplex session [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class CalculatorService : ICalculatorDuplex { double result; string equation; ICalculatorDuplexCallback callback = null; public CalculatorService() { result = 0.0D; equation = result.ToString(); callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>(); } public void Clear() { callback.Equation(equation + " = " + result.ToString()); result = 0.0D; equation = result.ToString(); } public void AddTo(double n) { result += n; equation += " + " + n.ToString(); callback.Equals(result); } ... }
client side :The client must provide a class that implements the callback interface of the duplex contract, for receiving messages from the service. The following sample code shows a CallbackHandler
class that implements the ICalculatorDuplexCallback
interface.
The WCF client that is generated for a duplex contract requires a InstanceContext class to be provided upon construction. This InstanceContext class is used as the site for an object that implements the callback interface and handles messages that are sent back from the service. An InstanceContext class is constructed with an instance of the CallbackHandler
class. This object handles messages sent from the service to the client on the callback interface.
public class CallbackHandler : ICalculatorDuplexCallback { public void Result(double result) { Console.WriteLine("Result({0})", result); } public void Equation(string eqn) { Console.WriteLine("Equation({0})", eqn); } } class Client { static void Main() { // Construct InstanceContext to handle messages on callback interface InstanceContext instanceContext = new InstanceContext(new CallbackHandler()); // Create a client CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext); Console.WriteLine("Press <ENTER> to terminate client once the output is displayed."); Console.WriteLine(); // Call the AddTo service operation. double value = 100.00D; client.AddTo(value); // Call the SubtractFrom service operation. value = 50.00D; client.SubtractFrom(value); // Call the MultiplyBy service operation. value = 17.65D; client.MultiplyBy(value); // Call the DivideBy service operation. value = 2.00D; client.DivideBy(value); // Complete equation client.Clear(); Console.ReadLine(); //Closing the client gracefully closes the connection and cleans up resources client.Close(); } }
The configuration for the service must be set up to provide a binding that supports both session communication and duplex communication. The wsDualHttpBinding element supports session communication and allows duplex communication by providing dual HTTP connections, one for each direction.
On the client, you must configure an address that the server can use to connect to the client, as shown in the following sample configuration.
Note: |
---|
Non-duplex clients that fail to authenticate using a secure conversation typically throw a MessageSecurityException. However, if a duplex client that uses a secure conversation fails to authenticate, the client receives a TimeoutExceptioninstead.
|
If you create a client/service using the WSHttpBinding element and you do not include the client callback endpoint, you will receive the following error.
HTTP could not register URL htp://+:80/Temporary_Listen_Addresses/<guid> because TCP port 80 is being used by another application.
The following sample code shows how to specify the client endpoint address in code.
WSDualHttpBinding binding = new WSDualHttpBinding(); EndpointAddress endptadr = new EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server"); binding.ClientBaseAddress = new Uri("http://localhost:8000/DuplexTestUsingCode/Client/");
or specify the client endpoint address in configuration. BUT If you use WSDualHttpBinding you dont need specify the client endpoint address.
<client> <endpoint name ="ServerEndpoint" address="http://localhost:12000/DuplexTestUsingConfig/Server" bindingConfiguration="WSDualHttpBinding_IDuplexTest" binding="wsDualHttpBinding" contract="IDuplexTest" /> </client> <bindings> <wsDualHttpBinding> <binding name="WSDualHttpBinding_IDuplexTest" clientBaseAddress="http://localhost:8000/myClient/" > <security mode="None"/> </binding> </wsDualHttpBinding> </bindings>
Sercurity Considerations
Endpoints exposing duplex services must be secured. When a service receives a duplex message, it looks at the ReplyTo in that incoming message to determine where to send the reply. If the channel is not secured, then an untrusted client could send a malicious message with a target machine's ReplyTo, leading to a denial of service of the target machine. With regular request-reply messages, this is not an issue, because the ReplyTo is ignored and the response is sent on the channel the original message came in on.
Something We need Know
Windows Communication Foundation supports allowing the service to call back to its clients. During a callback, in many respects the tables are turned: the service is the client and the client becomes the service.
The client also has to facilitate hosting the callback object. Not all bindings support callback operations. Because of HTTP's connectionless nature, HTTP can't be used for callbacks and therefore you can't use callbacks over BasicHttpBinding or WSHttpBinding. Windows Communication Foundation offers callback support for NetTcpBinding and NetNamedPipeBinding because the underlying transport is bidirectional. To support callbacks over HTTP, Windows Communication Foundation provides WSDualHttpBinding, which actually sets up two HTTP channels: one for the calls from the client to the service and one for the calls from the service to the client.
For Example . If you use the WSHttpBinding, you will got a exception message when opens the ServiceHost. Says WsHttpBinding do not support the Duplex or not right configuration . (If you used WSHttpBinding you have to specify the client endpoint address. which i metioned aboved. pls refer to it.)
Whenever interacting with a service endpoint whose contract defines a callback contract, the client must use a proxy that will set up the bidirectional communication and pass the callback endpoint reference to the service. The proxy the client uses must derive from the specialized proxy class DuplexClientBase<T>.
[System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.Samples.Duplex", ConfigurationName="Microsoft.Samples.Duplex.ICalculatorDuplex", CallbackContract=typeof(Microsoft.Samples.Duplex.ICalculatorDuplexCallback), SessionMode=System.ServiceModel.SessionMode.Required)] public interface ICalculatorDuplex { [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/Clear")] void Clear(); [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/AddTo")] void AddTo(double n); [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/SubtractFrom")] void SubtractFrom(double n); [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/MultiplyBy")] void MultiplyBy(double n); [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/DivideBy")] void DivideBy(double n); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public interface ICalculatorDuplexCallback { [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/Result")] void Result(double result); [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.Samples.Duplex/ICalculatorDuplex/Equation")] void Equation(string eqn); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public interface ICalculatorDuplexChannel : Microsoft.Samples.Duplex.ICalculatorDuplex, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public partial class CalculatorDuplexClient : System.ServiceModel.DuplexClientBase<Microsoft.Samples.Duplex.ICalculatorDuplex>, Microsoft.Samples.Duplex.ICalculatorDuplex { .... }
Note that the callback contract need not be marked with a ServiceContract attribute—it is implied.
The client-side callback endpoint reference is passed along with every call the client makes to the service and is part of the incoming message. The OperationContext class provides the service with easy access to the callback reference via the generic method GetCallbackChannel<T>. What exactly the service does with the callback reference and when it decides to use the reference is completely at the discretion of the service. The service can extract the callback reference from the operation context and store it for later use or the service can use the reference during the service operation to call back to the client.
this code example looks like below:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]//Attention this InstanceContextMode is different from the example before . the reason is below. class MyService : IMyContract { static List<IMyContractCallback> m_Callbacks = new List<IMyContractCallback>(); public void DoSomething() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel< IMyContractCallback>(); if(!m_Callbacks.Contains(callback)) { m_Callbacks.Add(callback); } } public static void CallClients() { m_Callbacks.ForEach(delegate(IMyContractCallback callback) { callback.OnCallback(); }); } }
just excute below code whenever you want.
MyService.CallClients();
so . (reason)Invoked this way, the invoking party is using some host-side thread for the callback invocation. That thread can be unrelated to any thread executing an incoming service call.(that is the good idea.)
If you just using the default ConcurrencyMode Which is Single-Threaded Mode. you will got the fault exception from the client app.
The following code demonstrates a service configured for reentrancy. During the operation execution, the service reaches into the operation context, grabs the callback reference, and invokes it. Control will only return to the service after the callback returns, and the service's own thread will need to reacquire the lock:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] class MyService : IMyContract { public void DoSomething() { IMyContractCa llback callback = OperationContext.Current. GetCallbackChannel<IMyContractCallback>(); callback.OnCallback(); } }
The third solution that allows the service to safely call back to the client is to have the callback contract operations configured as one-way operations. Doing so enables the service to call back even when concurrency is set to single-threaded, because there will not be any reply message to contend for the lock.
The good solution of using the publish-subscribe design pattern and decouple the publishers from the subscribers by introducing a dedicated subscription service and a dedicated publishing service in between
Questions:
Can we use WSHttpBind in Duplex Message ?
see also :
http://msdn.microsoft.com/en-us/magazine/cc163537.aspx (souce code in there)
posted on 2012-07-08 15:25 malaikuangren 阅读(536) 评论(0) 编辑 收藏 举报