Marchitecture - Real-time Data in HTML with Silverlight

This solution explains how to use a ‘headless’ Silverlight 2 solution to deliver ‘real-time’ or ‘push’ data into a browser.

Some of the limitations of HTTP can be overcome through the use of a long-lived HTTP connection allowing the streaming of real-time data to a client browser. This solution is an unconventional use of HTTP and may suffer from reliability problems. The following pattern describes ‘HTTP Streaming’: http://ajaxpatterns.org/HTTP_Streaming

Silverlight 2 has socket-based networking capabilities that can be used for this task in a more appropriate fashion. We could write a Silverlight UI that renders the data from the socket-based service, but we may not wish to amend an existing UI. So, if we combine the sockets capabilities with the Silverlight 2 HTML Bridge means that an existing web site can be integrated with Silverlight to provide true socket-based communication without the need to create a ‘Rich Internet Application’ UI, but still providing the ‘push’ capability.

Solution Components

image

Solution Description

A Silverlight 2 application (a ‘XAP’ file) can be embedded into a web page as a ‘zero-sized’ element, so doesn’t impact on an existing web page layout.

The XAP file can then set-up a socket connection to a data service. Once connected, the data service can push data updates as required (rather than needing to be polled in the case of an HTTP connection).

As data is received by the XAP, it can use the HTML bridging capabilities to either a) call a Javascript function with data or b) amend HTML content in the page directly. In the former case, the JavaScript function would be expected to handle the HTML content update.

The Detail

Several steps are needed to enable this scenario. These steps are described briefly in a simple example of a page wishing to receive

The Data Service

Suppose we have a ‘stock price service’ and we wish to feed ‘real –time’ data to an HTML page.

TcpListener listener = new TcpListener(IPAddress.Any,4510);
listener.Start();

Socket socket = listener.AcceptSocket();

 

The socket service must be running on a port between 4502 and 4532 with the current Beta 1 version of Silverlight 2.

Our sample service returns some fictitious stock data once every few seconds in the following format (we’re using XML here, though it’s quite likely a live service may have another format):

<?xml version="1.0" encoding="utf-8" ?>
<stocks>
          <stock code="MSFT" price="26"/>
          <stock code="AAPL" price="15"/>
</stocks>

 

The Silverlight 2 XAP

To take advantage of the data held by the socket service, we can create a Silverlight 2 application that contains no UI (other than a default page to handle the logic). In the constructor of the page, we create a socket connection.

DnsEndPoint endPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4510);

socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = socket;
args.RemoteEndPoint = endPoint;
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);

socket.ConnectAsync(args)

 

Once connected, we then set up a buffer and prepare to receive incoming data.

private void OnConnect(object sender, SocketAsyncEventArgs e)
{
   byte[] response = new byte[255];
   e.SetBuffer(response, 0, response.Length);
   e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnConnect);
   e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
   socket.ReceiveAsync(e);
}

 

When data is received, we can parse the data and decide what to do with it. In this instance, because the server is sending XML, we can use LINQ to parse the XML to a collection of objects.

private void OnReceive(object sender, SocketAsyncEventArgs e)
{
   string s = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);

   XDocument data = XDocument.Parse(s);

   var stocks = from stock in data.Descendants("stock")
                        select new StockData
                         {
                             Code = (string)stock.Attribute("code"),
                             Price = (double)stock.Attribute("price")
                         };

   List<StockData> stockList = stocks.ToList();

           // ...
}

 

The StockData class is a simple ‘data transfer object’ with properties corresponding to the XML data. This class is marked with the [ScriptableType] attribute to ensure that it can be marshalled by JavaScript in the host HTML page.

[ScriptableType]
public class StockData
{
   public string Code { get; set; }
   public double Price { get; set; }
}

 

Finally, now that we’ve received and parsed the data, we can make a call to a JavaScript function in the HTML page hosting the Silverlight XAP. Because the data was received asynchronously, we need to marshal the UI thread and pass the data on that thread to avoid a threading exception. This can be done by the use of the page Dispatcher and the use of a simple delegate.

private delegate void ReceiveCallback();

this.Dispatcher.BeginInvoke((ReceiveCallback)delegate { HtmlPage.Window.Invoke("printStockData", stockList); }, null);

 
The JavaScript Function

The JavaScript function is simple and is used to create a chunk of HTML in a standard AJAX-y way and push the generated HTML onto the page. Notice that we’re able to manipulate the ‘complex’ managed object (StockData) using standard JSON.

<script type="text/javascript">
  function printStockData(stockData){
    var stockHTML = "";
    for(var i=0; i<stockData.length; i++){
      stockHTML += "<li>" +stockData[i].Code +"&nbsp;<b>$ " +stockData[i].Price   +"</b></li>";
      }
      document.getElementById('stockInfo').innerHTML = '<ul>'+stockHTML +'</ul>';
    }
</script>

 

The HTML Content

The final piece of this particular solution is the HTML content. Our simple placeholder for stock information is as follows:

    <div>
        <h2>Market Data</h2>
        <ul id="stockInfo">
            <li>Loading...</li>
        </ul>
        <h3>The data is being pushed by the socket connection!</h3>
    </div>

 

Finally, the <object> tag needed to embed the Silverlight control onto the page looks as follows. Notice the zero size of the object.

<object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="0" height="0">
   <param name="source" value="ClientBin/HeadlessSockets.xap"/>
   <param name="onerror" value="onSilverlightError" />
   <param name="background" value="white" />
            
      <a href="http://go.microsoft.com/fwlink/?LinkID=108182" style="text-decoration: none;">
        <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
      </a>
</object>

 

The Output

This simple example demonstrates the use of Silverlight to perform non-UI specific functions: in this case the provision of socket (and push) capabilities to a browser window.

image

blog comments powered by Disqus