Well, Clemens threw the glove so here goes :-)
The first thing that we define is the contracts
[OperationContract(IsOneWay = true)]
void WriteString(string s);
[ServiceContract(CallbackContract = typeof(IClientSide))]
void Rotate(string s);
The client side contract defines a single method that the client needs to implement on his side (WriteString). The service side defines a single method that the service must implement (Rotate). The ServiceContract attribute on the IServiceSide interface defines this to be a duplex contract, and requires that the caller would implement the IClientSide interface.
Both methods are “one-way” methods. None-one-way methods might throw, so even when you call a void method you need to wait for it to complete before you can say that the operation was successful. One-way methods are true “fire and forget”, and do not wait for the operation to complete.
Ok, now for the client implementation:
class ClientImpl : IClientSide
public void WriteString(string s)
static void Main(string args)
ServiceSite mySite = new ServiceSite(new ClientImpl());
service = ChannelFactory.CreateDuplexChannel<IServiceSide>(mySite, "Default Client Config");
Console.WriteLine("Enter strings to rotate, or hit [Enter] to end.");
string str = " ";
while (str.Length > 0)
str = Console.ReadLine();
if (str.Length!=0) service.Rotate(str);
We first define a class that implements the client contract (ClientImpl). When the service operation WriteString is called, the implementation writes the string to the screen.
The Main method defines a local variable that would be used to reference the service (service) as well as a service site for the incoming calls (mySite). The service site is the object that would dispatch the calls made by the server to the instance of the client-side-contract-implementation-object that was passed in (the ClientImpl object created there).
Next, we create a duplex channel to the service, passing in the service site we created. The samples that Clemens and Bruce created hard-coded the server address and binding information into the app. Guys, come on, you know better J. Where’s the address and binding in this sample? They’re in the application configuration file. We’ll look at the configuration file format a little later.
We then go into a loop where we call the service with the string entered by the user. For each time we call the service, it would call us back multiple times on the duplex interface, resulting in the following output:
Enter strings to rotate, or hit [Enter] to end.
Now, let’s look at the service side.
class ServiceImpl : IServiceSide
public void Rotate(string s)
IClientSide client = OperationContext.Current.GetCallbackChannel<IClientSide>();
char chars = s.ToCharArray();
int len = chars.Length;
for (int i = 0; i < len; i++)
StringBuilder sb = new StringBuilder(len);
sb.Append(chars, len - i, i);
sb.Append(chars, 0, len - i);
The service implementation class (ServiceImpl) implements the service-side contract (IServiceSide). This contract defines a single method (Rotate). The first thing we do here is to get the callback interface to the client. This interface is exposed through the GetCallbackChannel method on the static member GetCallbackChannel of the class OperationContext, and it is set for us by Indigo before the infrastructure code calls into our implementation. We then have a loop where we rotate the string and call the client interface for each variation.
The code that hosts this service is:
ServiceHost<ServiceImpl> host = new ServiceHost<ServiceImpl>(new ServiceImpl());
Console.WriteLine("Service is ready. Press [Enter] to exit.");
In this code, we first create the host (again, the endpoints are defined in the service’s configuration file) and then open it. We then wait for clients to connect and call the service method, or for the user to hit Enter.
That’s it for the code. Now, let’s look at the configuration files:
Let’s start with the service’s configuration:
<?xml version="1.0" encoding="utf-8"?>
<service serviceType="DuplexService.ServiceImpl, DuplexService">
<endpoint contractType="DuplexSample.Interfaces.IServiceSide, DuplexService, Version=188.8.131.52, Culture=neutral, PublicKeyToken=null"
A single application can host many services. Those services are defined inside the <services> element. In our case, we have a single service. Each service can have one or more endpoints, defined using the <endpoint .../> element. This element defines the 3 elements of the endpoint (Address, Binding, and Contract) as follows:
o The address we use is a TCP address. The URI consists of the schema (net.tcp), host + port, and the path for the service. A single application can listen on multiple paths on the same TCP port.
o The contract type is a strongly named CLR type, consisting of the fully qualified interface name, the assembly in which it is defines, and the assembly version, culture, and public key token.
o The binding, referred to by type (in our case, a custom binding) and the name of the specific configuration section for that type (ServiceCustomBinding). For each type we can have multiple named configurations, and the name here helps distinguish between them.
The next section is the binding section. Each binding type has a section; in our case there’s a single binding type (we’re using a custom binding that we define ourselves (customBinding) and therefore a there’s a single binding section inside the bindings element. This custom binding has a single named configuration section that says that we’re only going to use TCP. In future samples I’ll show how to create more interesting custom bindings.
Now to the client configuration file:
<?xml version="1.0" encoding="utf-8" ?>
configurationName="Default Client Config"
contractType="DuplexSample.Interfaces.IServiceSide, DuplexClient, Version=184.108.40.206, Culture=neutral, PublicKeyToken=null">
The client side defines one endpoint that allows it to connect to the service. It specifies the same address as the service, the same contract (this one is compiled into a different assembly since the client and service do not share implementation – see if you can spot the difference). The binding type and configuration are defined in the same way they are defined on the service, and the interesting thing remaining is the configuration name. This is the name that was passed in when we called the ChannelFactory.CreateDuplexChannel method on the client. The client configuration file can contain many endpoints, but the client only picks one (by name) and uses it.
That’s it! Clemens, bring on the next challenge J