This article is compatible with the latest version of Silverlight.
This article is Part 3 of the series The Duplex Story in Silverlight.
In part 2, we looked at sockets. Using pure TCP for their communication, sockets make real duplexing possible: both sides of the communication channel can initiate communication. The server as well as the client can push data to the other side. There is no need to first have a request from the client to the server (which is the case with traditional HTTP).
While sockets are extremely fast, there’s a downside to that story as well: sockets in Silverlight only work on port numbers between 4502 and 4534. Therefore, sockets are great for intranet solutions but not for internet solutions. Also, we saw that working with sockets required an extensive amount of manual coding: there’s no support for WCF, so no proxies or whatever can be used in this scenario.
In this third and final part, we are going to look at the net.tcp binding. This binding was added to the Silverlight with the release of version 4. The great thing about this binding is that it combines some advantages of the polling duplex binding with sockets. In this article, we will recreate the sockets example from part 2, but now using net.tcp. The code can be downloaded here.
Before we dive into the code, we first need to perform some setup with our machine to support this binding.
With these steps completed, your system should be OK to use the net.tcp binding. Let’s now take a look at the net.tcp binding itself!
Advantages of using net.tcp
As already mentioned net.tcp combines advantages of both polling duplex and sockets. Its programming model is, just like polling duplex, based on WCF. That means that we can take advantage of automatic proxy generation inside Visual Studio and will have full IntelliSense with our client-side coding. On the other hand, it’s built on top of TCP. If you have read part 2, you’ll know that TCP stands for the best performance. And indeed, net.tcp has the same great performance. Here you can find a comparison graph between polling duplex and net.tcp. Some of the most striking conclusions are the following:
- Throughput of net.tcp on a worker thread of a Silverlight 4 application is 870x times faster than polling duplex.
- Throughput of net.tcp on a UI thread is 5.5x faster than polling duplex.
- This results is a far better scalability with the same hardware: up to 5x more clients can be connected at the same time with the server when using net.tcp.
On the other hand, again being based on TCP, net.tcp has the same port restrictions (4502-4534), making it a good intranet solution but not an internet solution.
Server-side code
With the entire configuration done, let’s write some code and let’s begin with the server-side code.
The service code is very similar to the service we wrote with the Http Polling Duplex. It defines a CallbackContract which is used from the server code to access the client instances. The operations are marked with the IsOneWay attribute to notify that there should be no waiting for the service response. For more information, refer to part 1.
The code below shows the service contract.
[ServiceContract(Namespace = "Silverlight",
CallbackContract = typeof(IStockServiceClient))]
public interface IStockService
{
[OperationContract(IsOneWay = true)]
void Connect(string stockSymbol);
}
[ServiceContract]
public interface IStockServiceClient
{
[OperationContract(IsOneWay = true)]
void SendUpdate(Update update);
}
The service implementation code is again similar to the duplex binding. We use the GetCallbackChannel() on the OperationContext to get an instance of the client callback contract. This can then be stored in a list that we loop over when updates need to be sent to the client. Below is the relevant part of the service implementation.
public void Connect(string stockSymbol)
{
client = OperationContext.Current.GetCallbackChannel<IStockServiceClient>();
updateTimer = new Timer(new TimerCallback(TimerTick), null, 500, 5000);
}
void TimerTick(object state)
{
if (client != null)
{
try
{
RefreshUpdate();
client.SendUpdate(update);
}
catch (Exception)
{
client = null;
updateTimer.Dispose();
}
}
}
One aspect that is different from the duplex binding is the configuration code. In the web.config, you can notice that the endpoint is now configured to be used with the nettcpbinding.
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<extensions>
<bindingExtensions>
<add name="pollingDuplexBinding"
type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex"/>
</bindingExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="DuplexCommunicationNetTcpBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceThrottling maxConcurrentSessions="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<pollingDuplexBinding>
<binding name="DuplexNetTcpBinding" useTextEncoding="true"/>
</pollingDuplexBinding>
<netTcpBinding>
<binding name="DuplexNetTcpBinding">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="DuplexCommunicationNetTcpBehavior"
name="DuplexCommunication.Web.StockService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:4502"/>
<add baseAddress="http://localhost"/>
</baseAddresses>
</host>
<endpoint address="" binding="pollingDuplexBinding"
bindingConfiguration="DuplexNetTcpBinding"
contract="DuplexCommunication.Web.IStockService"/>
<endpoint address="" binding="netTcpBinding"
bindingConfiguration="DuplexNetTcpBinding"
contract="DuplexCommunication.Web.IStockService"/>
<endpoint address="mex" binding="mexTcpBinding"
contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Client-side code
Because we are internally using WCF, we can now have Visual Studio create a proxy for us. Add a service reference to the StockService.svc (http://localhost/StockService/StockService.svc). Apart from the proxy that is created, some code will also be generated in the ServiceReferences.ClientConfig file, as can be seen below. Note that the net.tcp endpoint is now added here; communication will take place over net.tcp on port 4502.
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="NetTcpBinding_IStockService">
<binaryMessageEncoding />
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="net.tcp://w500:4502/StockService/StockService.svc"
binding="customBinding"
bindingConfiguration="NetTcpBinding_IStockService"
contract="StockService.IStockService"
name="NetTcpBinding_IStockService" />
</client>
</system.serviceModel>
</configuration>
With the service reference added, we can now use the service from client code as follows. Notice that this the normal way you would access a regular WCF service from in Silverlight.
private void StartButton_Click(object sender, RoutedEventArgs e)
{
StockService.StockServiceClient client =
new StockService.StockServiceClient("NetTcpBinding_IStockService");
client.SendUpdateReceived +=
new EventHandler<StockService.SendUpdateReceivedEventArgs>(client_SendUpdateReceived);
client.ConnectAsync("MSFT");
}
void client_SendUpdateReceived(object sender, StockService.SendUpdateReceivedEventArgs e)
{
StockTickingTextBlock.Text = (e.request as Update).Amount.ToString();
}
Summary
The net.tcp binding offers us a great deal for creating duplex communication from within a Silverlight application: while we still have the ability to work with WCF and benefit from all its advantages like proxy generation and IntelliSense, we also have the superb performance, courtesy of TCP on which it is based. Because of the latter, it’s only usable in intranet scenarios though, because of the port restrictions.
About the author
Gill Cleeren is Microsoft Regional Director (www.theregion.com), MVP ASP.NET, INETA speaker bureau member and Silverlight Insider. He lives in Belgium where he works as .NET architect at Ordina. Passionate about .NET, he’s always playing with the newest bits. In his role as Regional Director, Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF. He also leads Visug (www.visug.be), the largest .NET user group in Belgium. He’s also the author of “Silverlight 4 Data and Services Cookbook”, published by Packt Publishing. You can find his blog at www.snowball.be.