Blog from Saravanan Arumugam

Let us talk about Technologies

Category Archives: Duplex

Simple Duplex Channel Implementation


In WCF, we have three modes of message exchange, called Message Exchange Patterns (MEP)

  • Request/Response mode
  • One way message transfer
  • Duplex communication

Any default WCF service use the Request/Response MEP.

In places where we don’t expect any reply to come back from the receiver and where we simply trust that the communication channel would do its job right, we go for One way message exchange. In such cases even if an exception occurs at the receiving end we won’t receive it at the calling end. Example for such communication could be calling a logging service, Message queues etc.

The last one is the point of our interest in this paper, Duplex communication. By definition a duplex communication is, a communication between two parties with equal ability to exchange information. In terms of WCF, the service and client can talk to each other independently through two different end points. Upon client’s request to a service’s contract, the service would respond by calling a contract implemented in the client application itself.

WCF provides methods of implementing the duplex communication in a non-difficult but a little tricky way of coding. Let’s have a look at the very simple implementation of duplex communication.

  •  

    Step 1: Declare two services contracts, one for the duplex service and another for callback

    The very first thing is to create two service contracts. One is for the duplex service, the other one is the callback.

    In my example, I am going to create a service which will send a greeting message and the service would respond to the client with an acknowledgment.

    I consider it is important to mention where do I write each code. So on top of each code snippet let me write down if that’s done on client or server side.

  • //Written on Server Side

    namespace DuplexChannel
    {
        [ServiceContract(CallbackContract = typeof(IGreetingCallback))]
        public interface IGreetingService
        {
            [OperationContract(IsOneWay=true)]
            void SendGreeting(string message);
        }
     
        [ServiceContract]
        public interface IGreetingCallback
        {
            [OperationContract()]
            void GreetingReceived (string message);
        }
    }

    Here IGreetingService is the service contract and IGreetingCallback is the callback contract.

    You can note that the ServiceContractAttribute takes the CallbackContract as the named parameter. This is a notification that the contract is a duplex service contract. CallbackContract parameter takes the type of the callback , in our example IGreetingCallback.

    Note that the duplex operations should be one way (Note that IsOneWay=true in SendGreeting operation). If we miss mentioning the operation as one way, we would end up in a time out exception.

  •  

    Step 2: Define the duplex service on the server side (not the callback)

    Next step is to write the definition or implement the duplex service contract into class.

    An important thing to notice here is that we write definition only for the duplex contract, not for the call back. Because callback is the one to be defined on the client side.

    In the step 1 we have declared the structure of the IGreetingService and IGreetingCallback, but in this step we implement only the IGreetingService.

    //Written on Server Side
    namespace DuplexChannel
    {
        [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
        public class GreetingService : IGreetingService
        {
            [OperationBehavior()]
            public void SendGreeting(string message)
            {
                IGreetingCallback callback = OperationContext.Current.
    GetCallbackChannel<IGreetingCallback>();
                callback.GreetingReceived("Received the message: " + message);
            }
        }
    }

    In the SendGreeting you can see that we are expecting an instance of callback class. This has to come from the client side. Here the OperationContext.Current.GetCallbackChannel<T>() method retrieves the instance of callback class defined on the client side.

     

    Step 3: Specify the service behavior

    In the code snippet specified in step 2 we have provided a Service Behavior. Here we have explicitly marked the ConcurrencyMode to be Multiple. By default for any WCF service, the concurrency mode is Single. This means that only a single request would be serviced at a time.

    In the duplex service you can see that the current request (SendGreeting) has to be held in the middle while the call back instance (GreetingReceived) is signaled. So to do this, it either has to have an ability to serve multiple requests (One for the service itself and another one for the callback) or to leave the service in the middle (SendRequest) unprocessed, serve the other request (callback) and then come back to the original request (SendRequest) to complete it.

    The choice of this workflow is up to the design of the service. The workflow of serving multiple requests at the same time can be achieved by setting the concurrency mode as Multiple. Leaving a operation incomplete, jumping to another operation and coming back to the original operation can be achieved through Reentrant concurrency mode.

    So in summary the service behavior of a duplex service may look like this.

    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] 

    or

    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]

    Step 4: Configure the duplex service

    In the configuration there is no need for any special coding except for the type of binding to use.

    Following is the list of  bingings that support duplex channel.

    1. WsDualHttpBinding (for Http protocol)
    2. NetTcpBinding
    3. NetTcpContextBinding
    4. NetNamedPipeBinding
    5. NetPeerTcpBinding
    6. PollingDuplexBinding (introduced with/for Silverlight 4)

    Note: For a complete list of inbuilt bindings and their capabilities refer to System-Provided Bindings.

    In my example, I am going to use WsDualHttpBinding.

  • <!—- Written on server side –><system.serviceModel>
        <services>
          <service name="DuplexChannel.GreetingService">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:54927/"/>          
              </baseAddresses>
            </host>
            <endpoint address="ws" binding="wsDualHttpBinding" 
             contract="DuplexChannel.IGreetingService" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
              <!--The default setting-->
              <serviceThrottling maxConcurrentCalls="16" 
                     maxConcurrentInstances="1" 
                     maxConcurrentSessions="10"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>

    Step 5: Create service proxy

    Use service reference or svcutil.exe to create the proxy for the duplex service. This is an usual step.

    Unlike regular proxies, you can find the following changes in the duplex proxy class.

    • Usually the proxy class’s (e.g. GreetingServiceClient) base class would be ClientBase. But for duplex service, the base class would be DuplexClientBase.
    • We cannot find any empty constructor in the proxy class. Every constructor of proxy class would have a InstanceContext parameter. This parameter is inevitable because it has to presents the instance of callback class to the service.
        [System.Diagnostics.DebuggerStepThroughAttribute()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
        public partial class GreetingServiceClient : 
            DuplexClientBase<Messenger.GreetingService.IGreetingService>, 
            Messenger.GreetingService.IGreetingService {
            
            public GreetingServiceClient(InstanceContext callbackInstance) : 
                    base(callbackInstance) {
            }
            
            public GreetingServiceClient(InstanceContext callbackInstance, 
                string endpointConfigurationName) : 
                    base(callbackInstance, endpointConfigurationName) {
            }
            
            public GreetingServiceClient(InstanceContext callbackInstance, 
                string endpointConfigurationName, string remoteAddress) : 
                base(callbackInstance, endpointConfigurationName, remoteAddress) {
            }
            
            public GreetingServiceClient(InstanceContext callbackInstance, 
                string endpointConfigurationName,  EndpointAddress remoteAddress) : 
                base(callbackInstance, endpointConfigurationName, remoteAddress) {
            }
            
            public GreetingServiceClient(InstanceContext callbackInstance, 
                System.ServiceModel.Channels.Binding binding, 
                System.ServiceModel.EndpointAddress remoteAddress) : 
                    base(callbackInstance, binding, remoteAddress) {
            }
            
            public void SendGreeting(string message) {
                base.Channel.SendGreeting(message);
            }
        }

    Step 6: Modify client configuration to include client base address

    If the client base address is not explicitly specified WCF would start using address similar to http://+:80/Temporary_Listen_Addresses/634f73f6-c612-4441-acf8-79ddd5c62f98/

    Port 80 is usually occupied by the IIS. So the run time might throw an exception such as HTTP could not register URL … because TCP port 80 is being used by another application.

    It is a safe procedure to provide the client base address ourselves while designing duplex channel. Client base address can be provided in the binding configuration as follows.

    <configuration>
      <system.serviceModel>
        <bindings>
          <wsDualHttpBinding>
            <binding name="WSDualHttpBinding_IGreetingService"
                     clientBaseAddress="http://localhost:8088/clientCallbackUrl">
              <security mode="Message">
                <message clientCredentialType="Windows"
                         negotiateServiceCredential="true"
                         algorithmSuite="Default" />
              </security>
            </binding>
          </wsDualHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:54927/GreetingService.svc/ws"
            binding="wsDualHttpBinding" 
                     bindingConfiguration="WSDualHttpBinding_IGreetingService"
                      contract="GreetingService.IGreetingService" 
                     name="WSDualHttpBinding_IGreetingService">
            <identity>
              <userPrincipalName value="Saravanan-PC\Saravanan" />
            </identity>
          </endpoint>
        </client>
      </system.serviceModel>
    </configuration>

    Step 7: Start using the proxy

    In the previous section we have noticed that for initializing a proxy class we need to pass an instance of a class as a parameter. This parameter has to be the callback object which implements the callback contract declared in the server side. In my example IGreetingCallback.

    So typical declaration of a proxy would look like this.

    InstanceContext context = new InstanceContext(this);

    client = new GreetingServiceClient(context);

  •  

    Step 8: Build the Service Agent

    Though it is optional, its always a good practice to build a service agent which would act as wrapper to the service related activities. Service agent provides an extra layer with which we can isolate all the technical/formal tasks such as initializing proxy, providing callbacks, making asynchronous calls, closing proxies etc. This helps the client application to consider calling a service as easy of calling a simple C# method.

    Here is the Service agent I have written to handle both IGreetingService and IGreetingCallback.

        public class GreetingServiceAgent
            : IGreetingService,
              IGreetingServiceCallback,
            IDisposable
        {
            GreetingServiceClient client;
            public delegate void GreetingHandler(string message);
     
            public GreetingServiceAgent()
            {
                try
                {
                    InstanceContext context = new InstanceContext(this);
                    client = new GreetingServiceClient(context,
                                "WSDualHttpBinding_IGreetingService");
                    client.Open();
                }
                catch
                { client = null; }
            }
     
            public GreetingHandler GreetingRecievedHandler
            {
                get;
                set;
            }
     
            public void SendGreeting(string message)
            {
                if (client != null)
                    client.SendGreeting(message);
            }
     
            public void GreetingReceived(string message)
            {
                GreetingRecievedHandler(message);
            }
     
            public void Dispose()
            {
                if (client != null)
                    client.Close();
            }
        }

    Note that the GreetingServiceAgent implements both the IGreetingService and IGreetingServiceCallback.

    Since the ServiceAgent has implemented IGreetingServiceCallback, an instance of it becomes eligible candidate for the constructor of InsanceContext. That’s the reason we use ‘”this” object as the constructor parameter to InstanceContext. This instance context should be passed as a parameter to proxy initialization.

    It is also possible to create a different class for callback. In such a case the InstanceContext would be initiated with an instance of the callback class.

    Another thing is that GreetingReceivedHandler is provided as a public property which can be set as a delegate to any method that the client prefers.

    Client application has to write a method and set its delegate as GreetingReceivedHandler. When the service completes its operation, this delegate would be retrieved by the server and the server call this method as a callback.

  •  

    Step 9: Build the client application to call the service

    As a final step I have created a simple WPF application with a text block, text box and a send button. The text box would take the input message and text block would display the received message. The button would help calling the service with the message entered in the text box.

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
     
            private void Send_Click(object sender, RoutedEventArgs e)
            {
                if (!string.IsNullOrWhiteSpace(SendMessageTextBox.Text))
                {
                    GreetingServiceAgent agent = new GreetingServiceAgent();
     
                    agent.GreetingRecievedHandler += GreetingReceived;
                    agent.SendGreeting(SendMessageTextBox.Text);
                }
            }
     
            void GreetingReceived(string message)
            {
                ReceivedMessageTextBlock.Text = message;
            }
        }