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.

ms731064.note(en-us,VS.100).gifNote:
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.)

Duplex Mode Message Exchange Model_第1张图片

 

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 imported callback interface (Remember this interface is Created By Adding Service Reference Or using Svrutil tool , you can see it in the generated code.) will not necessarily have the same name as in the original service-side definition. Instead, it will be have the name of the service contract interface suffixed with the word Callback. (Pls notice It is the WCF proxy client code generated convention make it.)

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.)

Callback Reentrancy(Enable MultiThread. Good Idea.)
The service may also want to invoke the callback reference passed in or invoke the list of callbacks during the execution of a contract operation. However, such invocations are disallowed because by default the service class is configured for single-threaded access: the service instance is associated with a lock, and only one thread at a time can own the lock and access the service instance. Calling out to the clients during an operation requires blocking the service thread while invoking the callbacks. The problem is that processing the reply message from the client once the callback returns requires ownership of the same lock, and so a deadlock would occur.

If you just using the default ConcurrencyMode Which is Single-Threaded Mode. you will got the fault exception from the client app.

Duplex Mode Message Exchange Model_第2张图片 

To avoid a deadlock, if the single-threaded service instance tries to call back to its clients, Windows Communication Foundation will throw an InvalidOperationException. There are three possible solutions.
The first is to configure the service for multiple-threaded access, which would not associate it with a lock and would therefore allow callbacks. However, this would also increase the burden on the service developer because of the need to provide synchronization for the service.
The second solution is to configure the service for reentrancy. When configured for reentrancy, the service instance is still associated with a lock, and only a single-threaded access is allowed. However, if the service is calling back to its clients, Windows Communication Foundation will silently release the lock first.

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.

How about using Events. in place of Call Back? Good Idea.
The canonical use for duplex callbacks is with events. Events allow clients to be notified about something that occurred on the service side. The event may result from a direct client call or it may be the result of something the service monitors. The service firing the event is called the publisher, and the client receiving the event is called the subscriber.

 

Duplex Mode Message Exchange Model_第3张图片

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

 

Duplex Mode Message Exchange Model_第4张图片

Subscribers that want to subscribe to events register with the subscription service, which manages the lists of subscribers and also provides a similar ability to unsubscribe. Similarly, all publishers use the publisher service to fire their events and avoid delivering the events directly to the subscribers. The subscription and publishing services provide a layer of indirection that decouples your system. No longer do the subscribers have any knowledge about the identity of the publishers. They can subscribe to a type of an event and will receive the event from any publisher, since the subscription mechanism is uniform across all publishers. In fact, no publisher has to manage any subscription list and the publishers have no idea who the subscribers are. They deliver the event to the publishing service to be supplied to any interested subscriber.
You can even define two types of subscribers: transient subscribers are in-memory, running subscribers, and persistent subscribers are subscribers that persist on the disk, representing services to invoke when the event takes place. For transient subscribers you can use the duplex callback mechanism as a handy way of passing the callback reference to the running service. For the persistent subscribers, all you need to record is the subscriber address as reference. When the event is raised, the publishing service will call to the persistent subscriber address and deliver the event. Another important distinction between the two types of subscriptions is that you can store the persistent subscription on the disk or in a database. Doing so will persist the subscription across application shutdowns or machine crashes and restarts, thus enabling administrative configuration of the subscriptions. Obviously, you can't save the transient subscription across an application shutdown and you'll need to set up the transient subscription explicitly every time the application starts.
 
Queued Publishers and Subscribers
Instead of using synchronous binding for either publishing or subscribing to the events, you can use NetMsmqBinding. Queued publish-subscribe events combine the benefits of a loosely coupled system and the flexibility of disconnected execution. When using queued events, all events on the contract must, of course, be marked as one-way operations. As shown in Figure 15, you can use queuing at either end independently.

Duplex Mode Message Exchange Model_第5张图片

You can have a queued publisher and connected synchronous subscribers. You could have a connected publisher publishing to queued subscribers or you could have both queued publishers and queued subscribers. Note, however, that you can't have queued transient subscriptions because there is no support with the MSMQ binding for duplex callbacks. As before, you can use the administration tool to manage the subscribers and the administration operations are still connected and synchronous.
To utilize a queued publisher, the publishing service needs to expose a queued endpoint using the MSMQ binding. When firing events at a queued publisher, the publishing service can be offline or the publishing client itself can be disconnected. Note that when publishing two events to a queued publishing service there are no guarantees as to the order of delivery and the processing of these events by the end subscribers. You can only assume the order of publishing when the events contract is configured for a session and only when dealing with a single publishing service.
To deploy a queued subscriber, the persistent subscribing service needs to expose a queued endpoint. Doing so will enable it to be offline even when the publisher is online. When the subscriber is connected again, it will receive all its queued events. In addition, queued subscribers are ideal for when the publishing service itself is disconnected, because no events are lost. When multiple events are fired at a single queued subscriber, there are no guarantees as to the order of delivery of the events. The subscriber can only assume the order of publishing when the events contract is configured for a session. Of course, having both a queued publisher and subscriber allows them to work offline at the same time.
 
Windows Communication Foundation offers built-in support for powerful and useful programming models, using request-reply, one-way operations or duplex callbacks. Understanding the operations and choosing the right calling mode is one of the first and most important design decisions you will make, and yet applying each mode is surprisingly easy. You can use these operation types as-is in your service-oriented applications or compose your own higher-level abstractions such as the publish-subscribe pattern.

Questions:

Can we use WSHttpBind in Duplex Message ? 

see also :

http://msdn.microsoft.com/en-us/magazine/cc163537.aspx (souce code in there) 

 

你可能感兴趣的:(Exchange)