The next sample application shows how a duplex communication can be done between the client and the service. The client starts the connection to the service. After the client connects to the service, the service can call back into the client.
For duplex communication, a contract must be specified that is implemented in the client. Here the contract for the client is defined by the interface IMyMessageCallback. The method implemented by
the client is OnCallback (). The operation has the operation contract setting IsOneWay=true applied. This way, the service doesn’t wait until the method is successfully invoked on the client. By default, the
service instance can be invoked from only one thread (see the ConcurrencyMode property of the service behavior that is, by default, set to ConcurrencyMode. Single).
If the service implementation now does a callback to the client and waits to get an answer from the client, the thread getting the reply from the client must wait until it gets a lock to the service object. Because the service object is already locked by the request to the client, a deadlock occurs. WCF detects the deadlock and throws an exception. To avoid this situation, you can change the Concurrency Mode property to the value Multiple or Reentrant. With the setting Mul tiple, multiple threads can access the instance concurrently. Here, you must implement locking on your own. With the setting Reentrant, the service instance stays single threaded, but allows answers from callback requests to reenter the context. Instead of changing the concurrency mode, you can specify the IsOneWay property with the operation contract. This way, the caller does not wait for a reply. Of course, this setting is possible only if return values are not expected.
The contract of the service is defined by the interface IMyMessage. The callback contract is mapped to the service contract with the CallbackContract property of the service contract definition:
The class MessageService implements the service contract IMyMessage. The service writes the message from the client to the console. To access the callback contract, you can use the
OperationContext class. OperationContext. Current returns the OperationContext that is associated with the current request from the client. With the OperationContext, you can access session infonnation, message headers and properties, and, in case of a duplex communication, the callback channel. The generic method GetCallbackChannel() returns the channe of to the client instance. This channel can then be used to send a message to the client by invoking the method OnCallback( ) that is defined with the callback interface IMyMessageCallback. To demonstrate that it is also possible to use the callback channel from the service independent of the completion of the method, a new thread that receives the callback channel is created. The new thread sends messages to the client by using the callback channel.
Hosting the service is the same as it was with the previous samples, so its not shown here. However, for duplex communication, you must configure a binding that supports a duplex channel. One of the
bindings supporting a duplex channel is wsDuall:httpBinding, which is configured in the application configuration file:
With the client application,the callback contract must be implemented as shown here with the class ClientCallback that implements the interface IMyMessageCallback:
With a duplex channel, you cannot use the ChannelFactory to initiate the connection to the service as was done previously. To create a duplex channel, you can use the DuplexChannelFactory class. This class has a constructor with one more parameter in addition to the binding and address configuration. This parameter specifies an Instance Context that wraps one instance of the ClientCallback class. When passing this instance to the factory, the service can invoke the object across the channel. The client just needs to keep the connection open. If the connection is closed, the service cannot send messages across it.
Duplex communication is achieved by starting the service host and the client application.