Today topic cover about how to create a WCF service which uses named pipe communication channel but also support duplex communication. In a normal web service behavior, client sends request message to the server, the server process the request then respond the message back to the client, therefore, it always need to have a requester to call the web service. What if you need your service to automatically send response to the client, but the client does not require to send request as always? Duplex communication allow server to interact with the client.
For today blog post, I have created a simple Hello World WCF service that support duplex communication and uses NetNamedPipeBinding and then the service is hosted in IIS with WAS (Windows Process Activation Service). This Hello World service allows clients to subscribe itself to the service to allow the service spam them with "Hello World!" words.
First, let's create the service first, defining the service contracts and operation contracts. In order to create a duplex communication service, we need to create a duplex contract first. This duplex contract will be used by the client to implement the service callback method, which also mean the service will be firing this callback method to send response to the client.
Server Side
For my case, my service is going to spam "Hello World!" string to the client, so I define my duplex contract as follow:
public interface IHelloWorldServiceCallback
{
[OperationContract(IsOneWay=true)]
void
Spam(string message);
}
Note that I have set IsOneWay as true for the named parameter in my operation contract. It is because after the client subscribed itself to the server, server automatically send message to the client, server does not expect a reply from the client.
Then, define the normal service contract as follow:
[ServiceContract(
SessionMode=SessionMode.Required,
CallbackContract=typeof(IHelloWorldServiceCallback))]
public interface IHelloWorldService
{
[OperationContract]
void
SpamMe();
[OperationContract]
void
StopSpam();
}
Note that I have specified the Session Mode named parameter with SessionMode.Required. It is required when creating a duplex communication service because the server need to keep track the client session, and then during firing the callback method, the service know the response message should be sent to which correct client.
The following is the implementation of my Hello World service contract:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class HelloWorldService
: IHelloWorldService
{
private static Dictionary<string,
CancellationTokenSource> _sessionCTSs;
static
HelloWorldService()
{
//Create a single dictionary
to keep all cancellation token source for tasks
_sessionCTSs = new Dictionary<string,
CancellationTokenSource>();
}
public void
SpamMe()
{
//Client register a callback
channel session for itself to the server
var
callback = OperationContext.Current.GetCallbackChannel<IHelloWorldServiceCallback>();
//Get the client session Id
var
sessionId = OperationContext.Current.SessionId;
//Server spawn a new thread
that process and invoke the callback method
var
spamTaskCTS = new CancellationTokenSource();
Task.Run(()
=>
{
for
(int i = 0; i < 100; i++ )
{
if
(spamTaskCTS.IsCancellationRequested)
{
callback.Spam("Client
request stop spam!");
break;
}
callback.Spam("Hello
World!");
Thread.Sleep(1000);
}
}, spamTaskCTS.Token);
_sessionCTSs.Add(sessionId,
spamTaskCTS);
}
public void
StopSpam()
{
//Cancel the existing thread
base on Session Id
var
sessionId = OperationContext.Current.SessionId;
var
spamTaskCTS = _sessionCTSs[sessionId];
spamTaskCTS.Cancel();
}
}
The SpamMe() operation contract is to allow client register itself to the server that it want to subscribe the spam, asking the server to send message to it automatically. When the client invokes the SpamMe() operation, the client session ID will be kept in a dictionary. Then, a new thread will be created, the new thread will do the processing and invoke the callback method. The code above is actually sending "Hello World!" message to the client every one second for 100 times.
The StopSpam() operation contract is to allow client to request server to stop sending message to it. The method will get the client session ID from the same dictionary, and then cancel the sending message task. Once the task is cancelled, the service will not invoke the callback method anymore.
Host
Now, host the above service in IIS. The following is how my configuration file look like:
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="./HelloWorldService.svc"
service="NamedPipeDuplexSample.Services.HelloWorldService"
factory="System.ServiceModel.Activation.ServiceHostFactory"
/>
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="NamedPipeDuplexSample.Services.HelloWorldService"
behaviorConfiguration="">
<endpoint address="net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc"
binding="netNamedPipeBinding"
contract="NamedPipeDuplexSample.Services.Contracts.IHelloWorldService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing
metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive
exception details in faults for debugging purposes, set the value below to
true. Set to false before deployment to
avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The following are the steps to configure IIS to enable named pipe protocol. Before that, you need to ensure you have install WAS (Windows Process Activation Service) in your server first.
At the website level, right click and open the Edit Bindings menu, make sure you have net.pipe binding enabled as you see in the screenshot below.
Setup and host your service at the website that you had configured just now.
At your application level, right click then go to Manage Application, open Advanced Settings menu. Enter net.pipe to enable the named pipe protocol in your application as you see in the screenshot below:
Done.
Client Side
For the client side, I have created a simple console application to test my duplex WCF service. We need to implement the callback interface which we had defined in the server side code above.
public class HelloWorldServiceCallback
: IHelloWorldServiceCallback
{
public void
Spam(string message)
{
Console.WriteLine(message);
}
}
So, when the server invoke the callback method, this method at client side will be fired with the message from the server.
The following is the code of how to connect to the WCF service hosted in IIS with named pipe channel:
class Program
{
static void
Main(string[] args)
{
var
address = new EndpointAddress("net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc");
var
binding = new NetNamedPipeBinding();
var
callback = new HelloWorldServiceCallback();
var
context = new InstanceContext(callback);
var
factory = new DuplexChannelFactory<IHelloWorldService>(context,
binding, address);
var
service = factory.CreateChannel();
service.SpamMe();
Console.ReadKey();
service.StopSpam();
Console.ReadKey();
}
}
Once done, simply run the console program, then you will get the result as follow:
You even can run multiple console program to test the WCF session. Simply hit one key in one of the console program, only the correct one console will stop, the rest are remain untouched.
If you are interested with my source code, feel free to get it from HERE.
" Setup and host your service at the website that you had configured just now."
ReplyDeleteCare to elaborate please?
Is there an advantage of storing your cancellation token in a static dictionary instead of a non-static property?
ReplyDeletefor posterity: Each service class instance is running as "instance", and as threadpool is not running code outside the service class instance, there is an advantage if you want to implement complex logic outside service class instance. For example service that has internal maintenance thread running on it's own and you want to receive messages when this internal thread reaches specific points during its processing.
DeleteThe essential reason to have an expert to clean your carpet is that they are certified and professional too. This is immensely important because a certified carpet cleaner knows exactly how to clean every kind of carpet. Duct Replacement Services Melbourne
ReplyDelete