WCF – IIS Hosted Duplex Service & Winforms

WCF – IIS Hosted Duplex Service & Winforms

Author: Jeff Noble

 

NOTE: I HAVE CREATED AN EASIER WAY TO DO THIS. THIS ARTICLE IS STILL GOOD FOR AN UNDERSTANDING OF HOW THE WCF STUFF WORKS, BUT CREATING A SIMPLE PROJECT CHECK OUT THE NEWER UPDATED POST HERE:
http://savij.com/2009/12/12/updated-wcf-for-beginners/

Keywords: IIS Windows Communication Foundation WCF Windows Forms Duplex (I am looking for a good blog tool because this web editor sucks! I will reformat this article when I find a good one.) 

Recently I was tasked with comming up with a solution involving communication between servers and clients (including client to client communication). The idea is that as each client comes on-line (is launched) it should register itself with a central server. Then like a chat program, clients could communicate with each other. My first idea of course was to use LCS, it has all of these features and was already implemented within the company I work for.

After working with the API’s for a few days, I quickly realized that it was going to be messy. The calls were all COM components which would interfere with my simple ClickOnce deployments (See my ClickOnce article at: http://www.savij.com/2007/02/19/5/) and there was not an easy way within .net to send specific information between clients that was not simple text. More on that later. So after doing a bit of research into technology like Jabber and other things, I remebered some code I had recently seen about WCF. I decided this was the way to go.

I am going to describe and application that has a host and a client. The host is hosted in IIS and the client is a Winforms client. The idea is that I want to have the client application register itself with the host application and then have the host call back to the client. This will show how to set things up for WCF communication in a simple example, while having all of the plumbing in place to make the callback mechanism show how the client and host communicate. This application will NOT show you how to code the example application described in the beginning of this article, but you should have enough information by the end to complete an architecture of this nature on your own. Durring my first migrane from WCF I learned that there is not a whole lot of good documentation yet on WCF. Most of the examples are simple ones. I learned a BUNCH about it from DNRTV.

If you arent familiar with DNRTV check out the website at the same address as the name. It’s hosted by Carl Franklin just like DotNetRocks but it has video to show examples. A definite favs list addition IMHO.

Anyway, you can check out the WCF stuff here: http://www.dnrtv.com/default.aspx?showID=39. You are also free to get sample code from iDesign here and I reccomend that you do: http://idesign.net/idesign/DesktopDefault.aspx?tabindex=5&tabid=11#WCF.

Getting Started 

Ok, so here is the fast track to getting things working with WCF. First you MUST have the .net Framework 3.0 installed. There are many links with old versions flying around, so make sure you get the final version. I got mine here: http://www.microsoft.com/downloads/details.aspx?FamilyID=10cc340b-f857-4a14-83f5-25634c3bf043&DisplayLang=en Next it will behelpful to have the WCF extensions installed in Visual Studio 2005. This article is going to reference them. You can it from Microsoft here: http://www.microsoft.com/downloads/details.aspx?FamilyId=F54F5537-CC86-4BF5-AE44-F5A1E805680D&displaylang=en .

Now that everything is installed, create a new asp.net web site, then right click and add an app_code folder .

addcodefolder.png

The idea of WCF is actually kind of simple. In it’s simplest form, you need to create two interfaces. One of them describes your class (our host service) and the other one describes the callback. Our host service is going to describe a Register method for the client to call. It will store the client callback chanel so that it knows how to get calls back to the client. When it actually makes the callback, it will use the callback interface. Easy right? Right! Creating the Interfaces Create a new class in the App_Code folder of your host web application and give it a name. Mine is called RegistrationService. 

To keep things simple we will create an interface for our RegistrationService called RegisterClient, an interface for our callback and the service class which implements the service. Our service  will look like this:

[ServiceContract(CallbackContract = typeof(IMessagingCallback))]

public interface IRegistrationServices
{
[OperationContract(IsOneWay = true)]
void RegisterClient(string name);
}
public interface IMessagingCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveMessage();
}


[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class RegistrationService : IRegistrationServices
{
private IMessagingCallback _currentCallback = null;

static Dictionary _callbacks = new Dictionary();

#region IRegistrationServices
Members public void RegisterClient(string name)
{
OperationContext.Current.GetCallbackChannel().ReceiveMessage();
}
#endregion
}

Other than the fact that there are some attributes that may look foreign, the class is quite simple. At the top of the main interface, we must tell WCF that we have a Service Contract. This contract defines our service to our clients. Notice the attribute [ServiceContract(CallbackContract = typeof(IMessagingCallback))] at the top of the interface. It ties the IRegistrationServices interface to the IMessagingCallback interface. This is how we will identify the callback to the class. To keep it simple, all we do is put the ServiceContract attribute on the main interface, and then add [OperationContract] to the methods on the service that the client will be using. The IMessagingCallback interface gets the OperationContract as well. This will be implemented in the client. The RegistrationService class public class RegistrationService : IRegistrationServices implements the contract interface.

Notice that in the RegisterClient method we call back to the calling client and call the ReceiveMessage method on the actual client. We get to the client by accessing OperationContext.Current.GetCallbackChannel(). Well thats it for our Service class, we now have a class that will call back to it’s client when RegisterClient is called, and we have defined interfaces so that our client can talk to our service without having the class at the client location. More on that later. Before we go there are two more things we need in our server. First we need a file for IIS so that it knows this is a WCF service. Create a file in the root called Service.svc. In this file put the following code:
<% @ServiceHost Language=C# Debug="true" Service="RegistrationService" CodeBehind="~/App_Code/RegistrationService.cs" %>

Next edit your web.config file and add the following:
<system.serviceModel>
<services>
<service behaviorConfiguration="BaseBehavior" name="RegistrationService">
<endpoint binding="wsDualHttpBinding" bindingConfiguration="wsDualHttpBind" name="RegistrationService" contract="IRegistrationServices" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BaseBehavior">
<
serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" /> </behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsDualHttpBinding>
<binding name="wsDualHttpBind" sendTimeout="00:01:00"> <security mode="None">
<message clientCredentialType="None" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
</system.serviceModel>
There is tons of information on this file so I am not going to detail it. I will however point out the following: We defined a service name in this node (called RegistrationService):
<service behaviorConfiguration="BaseBehavior" name="RegistrationService">

Our endpoint which is our server connection is defined in this node:
<endpoint binding="wsDualHttpBinding" bindingConfiguration="wsDualHttpBind" name="RegistrationService" contract="IRegistrationServices" />

Our binding type is wsDualHttpBinding which shows support for the callback. This IS case sensitive, so be careful. It will try to match the node of the same name to get more information. It points the endpoint  to the <wsDualHttpBinding> node and then looks for the bindingConfiguration (ours is wsDualHttpBind and you can see that name in the binding node. The bottom line is that all this does is create a service that advertizes an endpoint. It’s not hard stuff, but the syntax must be correct. Again there are many samples of this online. You can also leave me questions at the end of this blog if you have any.

Thats all there is to the host, now on to the client!!!

Client Application

Add a new project to your solution of type Windows Application. Before we can write code to have this client register with the server, we need to create a proxy class. This class gives us our contract with the server, that is; we use the proxy within the client, and WCF proxies our calls over to the server. Unlike .net remoting which requires that we have a copy of the dll's on the client AND the server, WCF only requires a proxy at the client. To create the proxy you have three choices (that I know of). The first is to code it by hand. If you're as lazy as I am that options is out (unless of course I intend to self punish myself that day). The second option is to use a command line tool called SvcUtil.exe. In order to get it you need to d/l the Windows Platform SDK. I used the Windows Server 2003 R2 SDK. Using this tool is quite easy. The syntax is:
'SvcUtil.exe <Uri to server> /out:<proxyclassname>.cs /config:app.config'
The third choice and the one I have been using is the aforementioned WCF extensions for Visual Studio. This gives you right click ability inside Visual Studio for generating the proxy and modifying the app.config. Guess how it works .... it calls SvcUtil.exe. It's still more convienient for me.

After installing the WCF extensions just right click and Add Service Reference.

 In the next dialog box enter the URI of the server endpoint. Mine looks like this:

You now have all of the proxy and app.config changes you need to communicate with the server .... well, not exactly. There is a current issue when using duplex communication and IIS as a server host. The return trip from client to server will try to use port 80. Since that is bound to IIS if you tried it out now you would see the following:

To avoid this, you must specift a client address. This is a URI that points back to your machine. WCF will take care of creating the listener for you. Again there are two ways to do this. The first way is to add the attribute clientBaseAddress=
"http://jnoble.lefdomain.com:8002/ClientServices" to the <binding> in your app.config. The second way is to right click the App.config file and add the ClientBaseAddress in the dialog:

Again, WCF will take care of creating a listener at the endpoint you specify. The ':8002' in the above example will create a listener on my machine on port 8002. Make sure the port you choose is not blocked on your network and that you choose a port that is not already being used such as port 80 for IIS.

Now we are ready to code the client.

Coding The Client

Put the following member variables in the client form:

private SynchronizationContext _syncContext = null;
private RegistrationServicesClient _client = null;

Put the following code in the constructor of the client form:

_syncContext = SynchronizationContext.Current;
InstanceContext context = new InstanceContext(this);
_client = new RegistrationServicesClient(context);

 We need the SynchronizationContext object becasuse calls from the server need to be marshalled back onto the UI thread. If you don't set up the marshaling, you will be able to call to the server, but the return calls will just hang the client ... and that's bad mkay? Next we create an instance context, this is for WCF plumbing and needed for calls to the server. Finally we use the context to create our proxy object RegistrationServicesClient and store it in a variable called _client.

Add a button to the client form and put the following code in the click  event:

_client.RegisterClient("JNoble");

This will call RegisterClient in the proxy passing the name JNoble to the RegisterClient function on the server. If you look back at the code (or at the code at the top of this article) you will notice that RegisterClient() in the server does a callback to the client via:

OperationContext.Current.GetCallbackChannel().ReceiveMessage();

So calling RegisterClient ignored the parameter "JNoble" and just fires the callback on the client called ReceiveMessage(). If you wanted to you could pass the argument in the call to ReceiveMessage to actually pass the name. You would however need to add a string parameter to the interface on the server and re-generate the proxy to reflect the new argument.

Thats all there is to it! If you want to debug the project, here is the simplest way to do it (at least I think it's the simplest way).

  • Make the web project (the server) the Startup Project
  • Run the solution to start the web project.
  • Right click the client project and choose Debug -> Start New Instance.
     
  • Now you will hit both breakpoints on the client and server.

I am keeping this simple on purpose, if you want to learn more consult the documentation, this article is more aimed at getting up and running quickly.

I also will be blogging about DataContracts and how to send objects across the wire. I ran into some issues along the way, but it's all good now.

-Later

-Savij

14 Responses to “WCF – IIS Hosted Duplex Service & Winforms”

  1. crab Says:

    thx, ur article helped me to solve the problem i have been struggling for last 2 days… u rock :)

  2. Peter Says:

    How can U use duplex communication when Service is hosted on IIS and client isn’t visible in network (he’s behind NAT) ?

    I have no idea what value should I put in clientBaseAdress….

  3. savij Says:

    I have not tried it to a Nat’ client, but I was able to find this for you and it sounds like it should work just fine:
    http://www.topxml.com/rbnews/WSCF/WCF/re-38650_WCF-duplex-communication-with-unique-client-side-callback-addresses.aspx

  4. karthik Says:

    Hi, I want to provide security to IIS hosted WCF service. The client application is an windows application. I’m using wsDualHttpBinding. What would be best security option?

  5. savij Says:

    That depends on what type of security you are trying to provide. You can use HTTPS to encrypt the stream, but I don’t really have enough information to know if that covers the bases enough for your needs. There are many resources available to you on this subject.

    I would start with these two links:
    http://msdn2.microsoft.com/en-us/library/ms735093.aspx
    http://msdn2.microsoft.com/en-us/library/ms731925.aspx

    Hope that helps!

    -Jeff

  6. fhirzall Says:

    Great article, one of the simpler ones on WCF for starters. I’m getting this exception on button click when I try to register a client, any ideas? I’m using .net 3.5 if anything changed there. Thank you.

    An unhandled exception of type ‘System.InvalidOperationException’ occurred in System.ServiceModel.dll

    Additional information: The InstanceContext provided to the ChannelFactory contains a UserObject that does not implement the CallbackContractType ‘WCFClient.ServiceReference1.IHelloWorldServiceCallback’.

  7. fhirzall Says:

    I figured it out. I forgot to implement my interface on the form!

    Thanks!

  8. savij Says:

    Sorry I didnt get back to you. I was on vacation in Orlando and didnt get a chance to see the post. Glad you got it figured out!!!

    -J

  9. Pavan Kumar G Says:

    Hi,

    I am uanble to recieve a callback from server.I am getting timeout exception.

    Thanks
    Pavan

  10. savij Says:

    Pavan,

    How the hell do you expect me to reply to that one? You give no details, no exceptions and no insight. I really get annoyed with postings like this on forums and blogs. We are here to help, but come on!

    -J

  11. Hetal Says:

    Is that possible to use Duplex Service with XBAP (WPF browser app)?
    I am getting error for security. when I set up full trust and add temp certificate on IE. it works fine.

    • savij Says:

      Really not sure on this one. I have not messed with WPF to be an authority on it. I will leave your post up here and perhaps others can help shed some insight.

  12. YoG Says:

    I’m getting the same exception as fhirzall, but I don’t get the resolution… what does it means “implement my interface on the form”?

  13. savij Says:

    I think he meant on the form, just make sure it implements the callback interface or the service wont be able to see the form. Also, if you are using VS 2008 you can just create a WCF class and host it in a web project. It’s way easier now than when this article was written. Still, it’s nice to understand how it all works and how to manually do it.

    I also updated the article to use IsOneWay on the OperationContract attribute. This is needed for the callback to work now.
    -Jeff

Leave a Reply