Peter McGrattan’s Weblog

Silverlight, WCF, ASP.NET, AJAX, Graphics, RIA

Silverlight 2 WCF Polling Duplex Support – Part 1: Architecture

Posted by petermcg on September 3, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

Introduction

This is the first in a series of posts on the support for duplex communication with a WCF service added to Silverlight 2 Beta 2.  I should point out that this technology in its current form has been deemed by Microsoft as not yet fit for production applications and is therefore currently for evaluation only.  In this post I take a look at the general architecture of the technology and it’s approach to solving the problem of bi-directional communication between a server and a browser.  Parts two and three introduce the code for a client and server sample application that demonstrates the technology in practice.

The Assemblies

Support for bi-directional communication between a WCF Service and a Silverlight 2 Client over HTTP (port 80 by default) was introduced as part of the Silverlight 2 Beta 2 SDK. This is different to the Sockets infrastructure also supported by Silverlight 2 that requires a custom port within a specific range to be opened.  Two assemblies both named System.ServiceModel.PollingDuplex.dll provide this duplex support, one for the client, one for the server.  It’s clear to see which is which from the location each assembly is extracted to by the SDK installer:

%ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Client\System.ServiceModel.PollingDuplex.dll

%ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Server\Evaluation\System.ServiceModel.PollingDuplex.dll

Location aside, it’s possible to discern between the two based on the public key token of mscorlib.dll the assembly was built against (thanks to this post).  The client assembly is built against the Silverlight version, the server assembly against the .NET Framework version:

Client Assembly Manifest

Server Assembly Manifest

Both assemblies host types in two main namespaces familiar to WCF developers: System.ServiceModel and System.ServiceModel.Channels.  The content of the two assemblies in terms of types in these namespaces is similar but not identical.  The server side assembly has around nine additional internal types (PartialTrustHelpers, ContextQueue, IOThreadTimer, PollingDuplexChannelListener, RequestContextData, SafeNativeMethods, ServicePollingDuplexDispatcher, ServicePollingDuplexSessionChannel and UnsafeNativeMethods) with names that suggest they provide functionality outside of what can be achieved in the client sandbox.  The server assembly is therefore slightly larger than the client (101KB vs. 81KB) but from an API perspective they are identical in that each exposes only two public types: System.ServiceModel.PollingDuplexHttpBinding and System.ServiceModel.Channels.PollingDuplexBindingElement.  A little more exploration into these two types reveals that an instance of the latter is encapsulated by the former.  By this simple process of elimination it’s clear to see that creating an instance of the PollingDuplexHttpBinding type from both the client and server assemblies is the starting point to enabling WCF duplex communication in Silverlight 2 Beta 2.

The Binding

There are many different types of WCF Bindings, you can think of each one as a unique package of decisions with regard to the transport, message encoding, security and protocol details that will be employed to transfer a communication on the wire between a client and server or vice versa.  With these decisions encapsulated by the binding, enabling congruent communication between the client and server is simply a matter of instantiating the same binding type on both sides.  The PollingDuplexHttpBinding binding inherits from the standard WCF BasicHttpBinding class and specializes it to produce a new WCF binding specifically designed for communication with a Silverlight 2 client application.  The decisions packaged in the binding by default are to use HTTP as the transport, UTF-8 text as the message encoding, BasicHttpSecurityMode.None as the security setting (configurable in the constructor) and in addition to HTTP, Net Duplex and WS-Make Connection (WS-MC) as the protocols.  These protocols in turn rely on the XML and SOAP specifications, the Net Duplex Protocol is implemented to establish the Silverlight 2 client session with the server and WS-MC is used to establish a back channel using polling so that server initiated messages can be delivered to the client.  These protocols are specifically designed to be used in place of more traditional duplex protocols like TCP and cater for the fact that a Silverlight application running in a browser cannot accept new incoming connections unless a custom port is opened and agreed upon.  Another way to look at this is that without opening a custom port a Silverlight client application is not addressable by protocols such as TCP by default because the browser environment does not allow the creation of a listener. Consequently, with polling duplex communication the server must be addressable and the Silverlight application must initiate the connection to it.  In order to create the illusion of duplex communication (where either party can initiate a message exchange) in such a scenario, Microsoft have chosen to have the client poll the server for return messages after initiating the connection.

Polling and Timeouts

Once connected and until a fault or timeout occurs the client asynchronously polls the server more or less constantly give or take a few milliseconds between poll timeouts.  A poll returns when either there is a message to convey to the client or a poll-timeout occurs.  The default poll-timeout is currently around 60,000 milliseconds (60 seconds rather than 90 as you may have read elsewhere) but this can be altered by setting the PollTimeout property of an instance of PollingDuplexHttpBinding which will be forwarded to the identically named property of the binding’s encapsulated instance of PollingDuplexBindingElement.  When a PollTimeout occurs an exception is not raised unless either the client or server only has aborted the session during that time in which case the following exception is raised:

PollTimeout Exception

When the session has not been aborted and is still active, the occurrence of a PollTimeout does not raise an exception, an empty response (HTTP/1.1 200 OK with Content-Length:  0) is returned and a new poll originates on the network layer.  In the few milliseconds after a poll timeout occurs and before a new poll occurs, a queue of any messages to be sent to the client is maintained on the server ready to be flushed on the next poll.  There is another type of timeout that does raise an exception every time it occurs.  InactivityTimeout by default is set to 10 minutes (again configurable via the binding) and so will occur after 10 poll timeouts in a sequence, that is – after a period of 10 minutes during which no bytes have been sent or received by either party.  The following image shows the exception raised after an InactivityTimeout:

InActivityTimeout Exception

Anatomy of a Bi-Directional Message Exchange

In order to illustrate what I’ve covered so far and progress further a familiar context needs to be established in the form of a sample application.  The second and third posts in this series introduce the server and client parts of the sample application respectively and I encourage you to download the Visual Studio 2008 Solution, familiarize yourself with how it works and have a look at the code and comments.  The solution represents a simple client and server application that illustrates polling duplex communication in both directions.  The client displays stock prices in a grid and animates updates received from the server.  The user of the client application can also enter notes against a stock and have those notes propagate to all other connected clients via the server using polling duplex communication in the other direction.  Here is a screenshot of the sample application running with the server and two connected clients, the same stock price updates are being displayed by both clients and the notes entered in Internet Explorer have been pushed to another instance of the client running in Firefox:

Sample Application Running  

An excerpt of the most significant bi-directional conversation between one client and server assuming no communication problems would include the following events:

A. Client Connects To Server
B: Server Send Client An Empty Response
C. Client Polls Server For A Response
D. Server Sends Response Per Polling Client
E. Client Sends User Notes To Server
F. Server Sends Response Per Polling Client Including Notes

Tracing these events on the wire in practice results in the following traffic:

A. Client Connects To Server
POST /StockService HTTP/1.1
Accept: */*
Content-Length: 394
Content-Type: text/xml; charset=utf-8
SOAPAction: "Silverlight/IStockService/Register"
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
Host: 127.0.0.1:10201
Connection: Keep-Alive
Pragma: no-cache

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <netdx:Duplex xmlns:netdx="http://schemas.microsoft.com/2008/04/netduplex">
      <netdx:Address>http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=d640645c-4650-4889-8fd0-e7bf52156bcc</netdx:Address>
      <netdx:SessionId>e694f6c9-bab4-46a4-b81c-e5488609b48d</netdx:SessionId>
    </netdx:Duplex>
  </s:Header>
  <s:Body />
</s:Envelope>

This is a HTTP POST of the Net Duplex request from the client to the server to establish a unique session.  Note the presence of Connection: Keep-Alive and the SOAPAction in the HTTP request specifying the WCF service method to call (see part two for the server code).  The Net Duplex information is present as part of the SOAP header and includes two separate GUID identifiers in the Address and Session elements.  The Address element appears to specify an identifier for use by WS-MC (note the URI part of the identifier is the WS-MC anonymous URI although anonymous is spelt incorrectly) and the SessionId element appears to uniquely identify the current Net Duplex session; the SOAP body is empty.

B: Server Send Client An Empty Response
HTTP/1.1 200 OK
Content-Length: 0
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 01 Sep 2008 11:27:43 GMT

The server sends back an empty response (Content-Length: 0) indicating that the connection was successful but no messages destined for this unique session’s client are available at this time.

C. Client Polls Server For A Response
POST /StockService HTTP/1.1
Accept: */*
Content-Length: 318
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://docs.oasis-open.org/ws-rx/wsmc/200702/MakeConnection"
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
Host: 127.0.0.1:10201
Connection: Keep-Alive
Pragma: no-cache

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <wsmc:MakeConnection xmlns:wsmc="http://docs.oasis-open.org/ws-rx/wsmc/200702">
      <wsmc:Address>http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=d640645c-4650-4889-8fd0-e7bf52156bcc</wsmc:Address>
    </wsmc:MakeConnection>
  </s:Body>
</s:Envelope>

The above is a second HTTP POST to the server from the client a few milliseconds after the initial Net Duplex request.  This time the SOAPAction identifies it as a WS-MC MakeConnection request.  The SOAP envelope has no header but the body contains the same URI and GIUD identifier established in the Address element of the previous Net Duplex request.

D. Server Sends Response Per Polling Client
HTTP/1.1 200 OK
Content-Length: 578
Content-Type: text/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 01 Sep 2008 11:27:45 GMT

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <netdx:Duplex xmlns:netdx="http://schemas.microsoft.com/2008/04/netduplex">
      <netdx:Address>http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=d640645c-4650-4889-8fd0-e7bf52156bcc</netdx:Address>
      <netdx:SessionId>e694f6c9-bab4-46a4-b81c-e5488609b48d</netdx:SessionId>
    </netdx:Duplex>
  </s:Header>
  <s:Body>
    <Stock xmlns="urn:petermcg.wordpress.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Ask>256.7</Ask>
      <Bid>256.02</Bid>
      <Notes i:nil="true"/>
      <Symbol>AAHH</Symbol>
    </Stock>
  </s:Body>
</s:Envelope>

The above is the first real response sent back along the HTTP response channel or "back-channel" (a channel capable of carrying a SOAP message, without initiating a new connection) in reaction to the client’s connection request and polling.  This time the server has queued a message destined for this unique session’s client in the form of a serialized stock object with the symbol AAHH.  The content-length is therefore greater than zero this time as the response consists of a SOAP envelope with the Net Duplex information in the header and the serialized Stock object in the body. See the server code in part two for how the Stock objects are serialized using the DataContractSerializer.

E. Client Sends User Notes To Server
POST /StockService HTTP/1.1
Accept: */*
Content-Length: 607
Content-Type: text/xml; charset=utf-8
SOAPAction: "Silverlight/IStockService/Sync"
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
Host: 127.0.0.1:10201
Connection: Keep-Alive
Pragma: no-cache

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <netdx:Duplex xmlns:netdx="http://schemas.microsoft.com/2008/04/netduplex">
      <netdx:Address>http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=d640645c-4650-4889-8fd0-e7bf52156bcc</netdx:Address>
      <netdx:SessionId>e694f6c9-bab4-46a4-b81c-e5488609b48d</netdx:SessionId>
    </netdx:Duplex>
  </s:Header>
  <s:Body>
    <Stock xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:petermcg.wordpress.com">
      <Ask>227.69</Ask>
      <Bid>226.74</Bid>
      <Notes>Notes added from Internet Explorer</Notes>
      <Symbol>CZLP</Symbol>
    </Stock>
  </s:Body>
</s:Envelope>

This time we see a similar SOAP envelope to the previous response except travelling in the opposite direction.  The SOAPAction in the HTTP POST request is specifying that the Sync method of the WCF service should be called (again see part two for the server code).  The SOAP envelope again contains the identifying Net Duplex information in the header and the body contains a single serialized stock instance, this time with some notes entered by the user.

F. Server Sends Response Per Polling Client Including Notes
HTTP/1.1 200 OK
Content-Length: 607
Content-Type: text/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 01 Sep 2008 11:28:17 GMT

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <netdx:Duplex xmlns:netdx="http://schemas.microsoft.com/2008/04/netduplex">
      <netdx:Address>http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=d640645c-4650-4889-8fd0-e7bf52156bcc</netdx:Address>
      <netdx:SessionId>e694f6c9-bab4-46a4-b81c-e5488609b48d</netdx:SessionId>
    </netdx:Duplex>
  </s:Header>
  <s:Body>
    <Stock xmlns="urn:petermcg.wordpress.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Ask>227.69</Ask>
      <Bid>226.74</Bid>
      <Notes>Notes added from Internet Explorer</Notes>
      <Symbol>CZLP</Symbol>
    </Stock>
  </s:Body>
</s:Envelope>

On receipt of the notes post from the client, if there are no messages pending the server sends back an empty response as shown earlier.  A few milliseconds later when a client polls again or if there are other clients polling  as shown previously, the received update is echoed back in the response above.  To see the entire client and server code involved in generating these transmissions, download the sample Visual Studio 2008 solution attached to parts two or three.

Comet

Like all good problems, server to browser communication has prompted many slightly different solution implementations over the years.  The term Comet was first defined here (the technologies covered by the umbrella existed long before) as an umbrella term used to discuss these different implementations as a group when necessary (although it seems at one point it was nearly used as the codename for ASP.NET AJAX instead of Atlas).  More commonly the term is used in context with browser-native technologies such as JavaScript but I have seen it used to include proprietary plugins such as Silverlight.  The browser-native implementations apply methods of maintaining long lived connections between a browser and a server commonly using streaming or long-polling to enable partial page updates from JavaScript based on server initiated events.  If done correctly, this can result in near real time updates to a web-page without it ever explicitly requesting those updates.  Just some examples of applications that already use this kind of technology include:

Google Talk

Google Maps

Lightstreamer

Liberator

Kaazing

BlazeDS

Orbiter

Historically, browser-native Comet implementations have had to overcome several obstacles often based around the fact that the underlying technologies were not specifically designed to work in the way they were being manipulated.  Examples include browser compatibility, firewalls terminating long running connections and problems with incorrectly configured proxies.  Another obstacle is that keeping a long-lived connection open to a particular server uses up one of the simultaneous connections allowed by the HTTP 1.1 specification (have a look at the sixth paragraph under heading 8.1.4 here).  This can lead to browser usability problems but is often overcome by using a sub-domain on the same server as the initiator of the update events only.  It is very interesting to note that there are some changes around this restriction due in Internet Explorer 8 for broadband connections.  From a Silverlight perspective, as the implementation runs inside a plug-in the cross-browser compatibility obstacle is alleviated as long as the user has the plug-in installed.  These obstacles and their many different solutions by the different implementations of Comet in use today imply that no one solution is yet perfect across all browsers in all scenarios.  Work is underway to attempt to do something about this by standardising native support for server-sent events, event streaming and Web sockets in the HTML 5 specification, the latest Editor’s Draft at the time of writing is as up-to-date as a few days ago on 29 August 2008.  Native browser support for such communication in the future prompts musings on whether Microsoft could add not only polling but streaming duplex support to ASP.NET applications in the future using SOAP/JSON, the ASP.NET AJAX client framework, and an ASP.NET server control or WCF; and whether the messages would be compatible with this Silverlight framework on the wire and over the HTML bridge.

Posted in Comet, PollingDuplex, Silverlight, WCF | Tagged: , , , , , , , , , | 13 Comments »

Silverlight 2 Pie Chart (Beta) Updated

Posted by petermcg on August 10, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

I recently returned from a break and had some time to add a few minor features to the pie chart beta.  If you have any ideas for features not included in this update, now would be a good time to let me know.

IsDoughnut Property and GapScale

One comment recently requested the ability to create a doughnut chart in Silverlight Beta 2.  This functionality has now been added and can be switched on or off via the IsDoughnut property.  The size of the hole in relation to the size of the pie-chart can also be controlled via the GapScale property:

<charts:PieChart x:Name="basicPie" Center="150.0, 150.0" Radius="100.0" IsDoughnut="True" GapScale="0.25"
                 Stroke="LightGray" StrokeThickness="2.0" FontSize="10">
    <charts:PieSegment Percentage="35" Foreground="Orange" />
    <charts:PieSegment Degrees="38" Foreground="Blue" />
    <charts:PieSegment Degrees="72" Foreground="Green" />
    <charts:PieSegment Degrees="101" Foreground="Red" />
    <charts:PieSegment Degrees="23" Foreground="Purple" />
</charts:PieChart>

This produces a chart as follows :

Basic Doughnut Pie Chart

When the IsDoughnut property is set to true, each segment is now comprised of four Points and two ArcSegments:

// Segment Geometry
double pieRadius = this.pieChart.Radius;
double gapRadius = pieRadius * ((this.pieChart.GapScale == 0.0) ? 0.35 : this.pieChart.GapScale);

Point A = GetCircumferencePoint(startAngle, pieRadius);
Point B = (this.pieChart.IsDoughnut) ? GetCircumferencePoint(startAngle, gapRadius) : this.pieChart.Center;
Point C = GetCircumferencePoint(endAngle, gapRadius);
Point D = GetCircumferencePoint(endAngle, pieRadius);

bool isReflexAngle = Math.Abs(endAngle - startAngle) > 180.0;

PathSegmentCollection segments = new PathSegmentCollection();
segments.Add(new LineSegment() { Point = B });

if (this.pieChart.IsDoughnut)
{
    segments.Add(new ArcSegment()
    {
        Size = new Size(gapRadius, gapRadius),
        Point = C,
        SweepDirection = SweepDirection.Clockwise,
        IsLargeArc = isReflexAngle
    });
}

segments.Add(new LineSegment() { Point = D });
segments.Add(new ArcSegment()
{
    Size = new Size(pieRadius, pieRadius),
    Point = A,
    SweepDirection = SweepDirection.Counterclockwise,
    IsLargeArc = isReflexAngle
});

Path segmentPath = new Path()
{
    StrokeLineJoin = PenLineJoin.Round,
    Stroke = this.pieChart.Stroke,
    StrokeThickness = this.pieChart.StrokeThickness,
    Data = new PathGeometry()
    {
        Figures = new PathFigureCollection()
        {
            new PathFigure()
            {
                IsClosed = true,
                StartPoint = A,
                Segments = segments
            }
        }
    }
};

The code above shows how a 2D Path (segmentPath) object is assembled for either a single pie-chart segment or a single doughnut segment.  A close look reveals four Point objects named A,B,C,D and two ArcSegment objects, one drawn in a Clockwise direction and the other in the default CounterClockwise direction.  The following image illustrates the location of these Points and the two ArcSegment objects when the IsDoughnut property is set to true.

Doughnut Arc Points

For a normal pie-chart segment (the default or when IsDoughnut is false) examination of the code above reveals that the Clockwise ArcSegment ending in point C is not required and that Point B becomes the center of the pie-chart.

Label Location, Color and Visibility

The labels can now be displayed inside or outside of the segments and their color and visibility can be controlled via the LabelLocation, LabelStroke and LabelVisibility properties respectively :

<charts:PieChart x:Name="linearPie" Center="150.0,150.0" Radius="100.0" Grid.Row="1"
                 Stroke="LightGray" StrokeThickness="2.0" FontSize="10" LabelLocation="Inside">
    <charts:PieSegment Degrees="45" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Percentage="20" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Degrees="225.0" Foreground="{StaticResource TwoColorLinearBrush}" />
    <charts:PieSegment Percentage="5" Foreground="{StaticResource TwoColorLinearBrush}" />
</charts:PieChart>

This produces the following chart :

Labels Inside

There have also been a few smaller updates in areas such as the MouseEnter animation and the addition of extra buttons to the demo to demonstrate changing values at runtime.  If you have an idea you would like to see in a future beta of this control, please let me know.

Posted in Pie Chart, Silverlight | Tagged: , , , , , , , | 6 Comments »

Silverlight Initialization via the ASP.NET Silverlight Server Control

Posted by petermcg on July 16, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

This post takes a look at the ASP.NET Silverlight server control and what it renders as markup in response to a web request when added to an ASP.NET page.  It then digs deeper to explore how this markup describes the Silverlight plug-in and how this rendered plug-in is itself initialized in the Microsoft ASP.NET AJAX client framework.  This post also shows how to specify and retrieve Silverlight initialization parameters when using the server control.

The ASP.NET Silverlight Server Control

The Silverlight control is one of two ASP.NET server controls included in the Silverlight 2 Beta 2 SDK; the other is the MediaPlayer control which actually inherits from the Silverlight control.  The control provides an easy way to include Silverlight content in a new or existing ASP.NET Web page using the familiar ASP.NET server control model.  The control is available in the System.Web.Silverlight.dll assembly located under %ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Server in the System.Web.UI.SilverlightControls namespace.  The Silverlight content to be executed by the plug-in this control ultimately renders can be specified by setting the value of the Source property to a URI either to a XAML file or in Silverlight 2 a compiled Silverlight application (a .xap file):

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/InitParameters.xap"
                 MinimumVersion="2.0.30523" Width="100%" Height="100%" />

The Silverlight server control requires access to a ScriptManager control (generally on the same page) otherwise an InvalidOperationException will be thrown at runtime when the control initializes.

Rendered Markup and the ASP.NET AJAX Client Framework

When rendered to the output stream as part of an ASP.NET page it is declared in, the Silverlight server control delivers four additions to the markup eventually interpreted by the browser.  The first addition the control renders is a JavaScript script block under the html <body> tag with it’s src property set to a path that includes the string "ScriptResource.axd":

<script src="/InitParametersWeb/ScriptResource.axd?d=wMEjThkBoZWGT...;t=633481889860000000"
        type="text/javascript"></script>

This resource is requested by the Silverlight server control in it’s implementation of the IScriptControl interface.  When this script block is encountered as the html page loads a GET request is made to resolve the path assigned to the src property; because the path includes the string "ScriptResource.axd", the request is handled by an instance of the ScriptResourceHandler class as mapped in the default web.config file under the httpHandlers element:

<httpHandlers>
    <add verb="GET,HEAD"
       path="ScriptResource.axd"
       type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0,
             Culture=neutral, PublicKeyToken=31BF3856AD364E35"
       validate="false"/>
</httpHandlers>

The ScriptResourceHandler class implements the IHttpHandler interface in a way that handles request processing for script files that are embedded as resources in an assembly.  In this case the handler eventually resolves the path to a gzip compressed version of either the SilverlightControl.js or SilverlightControl.debug.js JavaScript file (depending on the ScriptMode property of the required ScriptManager control and in turn on the compilation mode in web.config) embedded in the System.Web.Silverlight.dll assembly:

System.Web.Silverlight Resources

It’s the JavaScript code in this file that (amongst other things) registers the Sys.UI.Silverlight ASP.NET AJAX client namespace, the definition of the Sys.UI.Silverlight.Control client component class (with Sys.UI.Control as the base class) and the definition of a method called createObject.

The second addition to the markup is a html <span> element:

<span id="Xaml1_parent"></span>

This <span> element is referenced in the third piece of markup the Silverlight server control renders. Another JavaScript script block, this time with some in-line code that calls the previously defined createObject method:

<script type="text/javascript">

//<![CDATA[

Sys.UI.Silverlight.Control.createObject('Xaml1_parent', '<object 
type="application/x-silverlight-2-b2" data="data:application/x-silverlight-
2-b2," id="Xaml1" style="height:100%;width:100%;">\r\n\t<param
name="MinRuntimeVersion" value="2.0.30523">\r\n\r\n\t</param><a
href="http://go2.microsoft.com/fwlink/?LinkID=114576&amp;v=2.0"><img
src="http://go2.microsoft.com/fwlink/?LinkID=108181" alt="Get Microsoft
Silverlight" style="border-width:0;" /></a>\r\n</object>'
); //]]> </script>

The createObject method takes two parameters here, the first is a string specifying the id of a parent element (the <span>) the second parameter is a string containing html markup for the Silverlight plug-in using the <object> element. The body of the createObject method finds the parent element on the page using the id in the first parameter and sets it’s innerHtml property to be the second parameter. When the innerHTML property is set, the <object> element becomes the content of the <span> element resulting in the object tag describing the Silverlight plug-in becoming part of the browser’s DOM.

The fourth and final markup snippet rendered by the ASP.NET Silverlight server control is the in-line JavaScript script statement shown below:

Sys.Application.add_init(function() {
    $create(Sys.UI.Silverlight.Control, {"source":"ClientBin/InitParameters.xap"},
            null, null, $get("Xaml1_parent"));
});

This script statement adds an anonymous JavaScript function as an event handler for the init event of the Sys.Application client object defined by ASP.NET AJAX. Handlers for this init event are executed after the DHTML window object’s onload event and after the ASP.NET AJAX runtime has been initialized.  The body of this anonymous function contains a single statement that invokes the $create alias defined by the Microsoft ASP.NET AJAX library in the MicrosoftAjax.js or MicrosoftAjax.debug.js JavaScript file embedded in the System.Web.Extensions.dll assembly.

The $create alias is just an convenience for calling the Sys.Component.create function which performs a number of steps.  It creates an instance of the Sys.UI.Silverlight.Control client class within the ASP.NET AJAX client framework as a visual component associated with the span element (this association is required for visual controls) returned as the last parameter from the call to the $get alias. Crucially it sets the value of the ‘source’ property on this component instance to the .xap file using the key/value object literal generated from the server control ‘Source’ property and passed as the second parameter.  It calls the initialize method inherited from the base Sys.UI.Control class on the component instance and finally stores a reference to the instance as a child component of the global Sys.Application object via it’s addComponent method.

At this stage I believe the Silverlight-plug-in, now part of the browser DOM and registered as a visual component in the Microsoft ASP.NET AJAX client framework with it’s source property set to the .xap file, performs the following steps outlined in the Silverlight 2 Beta 2 documentation:

"…starts the common language runtime (CLR) and creates an application domain for the Silverlight application to run in. Downloads the application package, works with the Silverlight runtime to create an execution environment for the Silverlight application, and loads the assemblies and resource files included in the application package. The next step is to instantiate the application class to start the application running."

The Silverlight Application Class

Several of Microsoft’s API’s follow a similar pattern of having an instance of an Application type as the entry point into the program. The client-side ASP.NET AJAX framework, the ASP.NET server-side framework, WPF and Silverlight 2 all employ this pattern. The Application class is the one class that every Silverlight application must implement.  When you create a ‘Silverlight Application’ using the default Visual Studio 2008 template, the Application class is inherited by the ‘App’ class defined in the App.xaml and App.xaml.cs files.  It’s the instance of this App class that initializes and starts the main UI of the Silverlight Application by creating an instance of the Page class (defined in Page.xaml and Page.xaml.cs) and assigning it to the RootVisual property.

Initialization Parameters

To specify initialization parameters to Silverlight from a basic Html page the options are to either set the initParams parameter to a correctly formatted string using JavaScript against the Silverlight plug-in’s object model, or to set the value of a <param> element named InitParams to the same formatted string under the Silverlight plug-in’s <object> tag using Html.  The formatted string should match the pattern : "key1=value1,key2=value2,key3=value3".

The same functionality can be achieved when hosting Silverlight using the ASP.NET Silverlight server control by setting the server-side InitParameters property to the exact same formatted string.  The InitParameters server control property is of type string, is stored in ViewState, and can be set in markup and code-behind; it’s important that this string is in the expected format to ensure the parameters make it to Silverlight.  When a Silverlight server control with it’s InitParameters property set ultimately renders itself, the string is appropriately escaped and passed on to become the value of a <param> element named InitParams inside the generated Silverlight plug-in <object> element.

The key/value pairs in this formatted string are parsed into an IDictionary<string, string> and are able to be retrieved in Silverlight 2 in the StartupEventArgs.InitParams property (e.InitParams) in an event handler for the Application.Startup event:

Application Startup with Parameters

The sample visual studio solution available for download above demonstrates how to specify initialization parameters in both markup and code behind on a Silverlight ASP.NET server control and how to retrieve those parameters in an event handler for the Silverlight Application.Startup event.  The sample also demonstrates passing the parameters on to an instance of the Page class to be displayed in a DataGrid.

Posted in ASP.NET, Silverlight | Tagged: , , , , , , , , | 14 Comments »

Stock List Demo Optimised

Posted by petermcg on July 4, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

I’ve recently addressed some comments on the blog and also optimised the Stock List Demo sample application, particularly the serialization.  The updates or deltas can now be pushed to clients at intervals from approx 1 millisecond.  If you haven’t seen this demo or haven’t downloaded it in a while it’s worth taking a look at the latest version.  Here is a screenshot of the server and one client application running :

Application Running

The sample comprises a Silverlight application with a DataGrid bound to an ObservableCollection<T> initially populated from a call to a Wcf Service and then updated by deltas received over Tcp Sockets.  The sample as a whole provides code that demonstrates some interesting different areas of Silverlight 2 development.  Silverlight Sockets support is used to push updates to one or many connected clients now at intervals from approx 1 millisecond upwards (the sample defaults to 50 milliseconds but feel free to change this to speed the sample up or slow it down) :

// Server
private void ServeDeltas()
{
    while (true)
    {
        if (synchClients.Count > 0)
        {
            // Send latest delta to all subscribed clients
            synchClients.SendAll(GetLatestDelta());

            // Set this to anything from 1.0 millisecond
            Thread.Sleep(TimeSpan.FromMilliseconds(50.0));
        }
        else
        {
            clientEvent.WaitOne();
        }
    }
}

The Silverlight UI remains responsive and notes can still be added against stocks in the DataGrid at these speeds.  The serialization has been optimised to use the DataContractSerializer and MemoryStream classes to more efficiently and reliably serialize and deserialize Stock objects from server to client :

// Server
public void Send(Stock stock)
{
    byte[] bytes = new byte[1024];

    using (MemoryStream memStream = new MemoryStream(bytes))
    {
        xmlSerializer.WriteObject(memStream, stock);

        try
        {
            memStream.WriteTo(netStream);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

// Client
private void OnSocketAsyncEventCompleted(object sender, SocketAsyncEventArgs e)
{
    Socket socket = e.UserToken as Socket;
    byte[] buffer = new byte[1024];

    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Connect:
            // Set up receive buffer
            e.SetBuffer(buffer, 0, buffer.Length);
            // Asynchronously request to receive data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.None:
            break;
        case SocketAsyncOperation.Receive:
            // Deal with received data from the server in our buffer
            int bytesTransferred = e.Buffer.TakeWhile(b => b != byte.MinValue).ToArray().Length;

            using (MemoryStream memStream = new MemoryStream(e.Buffer, 0, bytesTransferred, false))
            {
                try
                {
                    Stock stock = xmlSerializer.ReadObject(memStream) as Stock;

                    Action<Stock> action = UpdateStockList;
                    owner.BeginInvoke(action, stock);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

            // Clear receive buffer
            e.SetBuffer(buffer, 0, buffer.Length);

            // Asynchronously request to receive more data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.Send:
            break;
        default:
            break;
    }
}

The sample also shows how to use the Silverlight Dispatcher and Action<T> to queue work for execution on the UI thread (see client code after ReadObject above) and also how to apply an Action<T> to each element in a List<T> using ForEach :

// Client
private void OnGetStocksCompleted(object sender, GetStocksCompletedEventArgs e)
{
    Action<Stock> action = delegate(Stock delta)
    {
        StockDecorator stock = stockList.FirstOrDefault(s => s.Symbol == delta.Symbol);

        if (stock == default(StockDecorator))
        {
            stockList.Add(new StockDecorator(delta));
        }
    };

    e.Result.ForEach(action);
}

The Silverlight Socket policy requirements are handled on a separate worker thread in the same server that sends the Stock updates :

// Server
private void ServePolicy()
{
    while (true)
    {
        Console.WriteLine("Accepting Policy Requests...");

        // Read policy file
        byte[] policyBytes = File.ReadAllBytes("PolicyResponse.xml");

        using (Socket client = policyListener.AcceptSocket())
        {
            Console.WriteLine("Policy Request Accepted...");

            // Get poilicy request header
            byte[] buffer = new byte[1024];
            int bytesReceived = client.Receive(buffer);

            // Basic check of request header
            string header = Encoding.UTF8.GetString(buffer, 0, bytesReceived);

            if (header == "<policy-file-request/>")
            {
                client.Send(policyBytes, 0, policyBytes.Length, SocketFlags.None);
            }
        }
    }
}

The animation of the Stock Updates (green for positive, red for negative) is created programmatically and the sample also shows how to bind to those animations from Xaml :

<data:DataGrid x:Name="StocksGrid"
               AutoGenerateColumns="False"
               Grid.Row="1"
               Grid.ColumnSpan="2"
               GridlinesVisibility="Horizontal"
               HeadersVisibility="Column"
               RowBackground="AliceBlue"
               IsReadOnly="True"
               CanUserResizeColumns="False"
               Margin="0,5,0,0">
    <data:DataGrid.Columns>
        <data:DataGridTemplateColumn Header="Symbol">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Symbol}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Bid">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding BidHighlight}">
                        <TextBlock Text="{Binding Bid}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Ask">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding AskHighlight}">
                        <TextBlock Text="{Binding Ask}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn MinWidth="150" Header="Notes">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Notes}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

Multiple clients from different browsers can be connected by firing up a new browser and copy/pasting the address, they will receive the same updates (notice the highlights in the screenshot below) :

Multiple Clients Connected

The server will pause using an AutoResetEvent if there are no clients connected and resume when at least one client connects (you can also pause the server by pressing the pause key on your keyboard and resume by pressing Enter).  If you are working on a proof on concept for Silverlight 2 hopefully this is an example of a substantial application which may be helpful.

Posted in Silverlight, Sockets, WCF, XAML | Tagged: , , , , , | 12 Comments »

Trivia : Argentum

Posted by petermcg on June 16, 2008

You may have seen the Ag prefix used in various contexts with Silverlight such as in the naming of agcore.dll, and the Upcoming AgDataGrid Suite for Silverlight 2 Beta 2.  The reason Ag is used is that it’s the symbol in the periodic table for the transition metal Silver, originally from the Latin Argentum 🙂

Posted in Silverlight | Tagged: , , | Comments Off on Trivia : Argentum

Beta 2 Xaml Namespaces

Posted by petermcg on June 16, 2008

The default XML namespace used by XAML in Silverlight 2 has changed in the transition from Beta 1 to Beta 2.  The two namespaces defined by default in Beta 1 were as follows :

<UserControl xmlns="http://schemas.microsoft.com/client/2007"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

In Beta 2 the default namespace has changed :

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

The change has more than likely occurred as part of the work to make the Silverlight 2 API more compatible with WPF, although WPF 3.5 does define a new XML namespace :

xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"

Compatibility should still be maintained however as this namespace or the namespace defined in WPF 3.0 can be used when building WPF 3.5 applications.

XML Namespaces

An XML namespace must be a valid Uniform Resource Identifier (URI), a URI can either be a Uniform Resource Locator (URL) as in the above examples or a Uniform Resource Name (URN) as in the example below :

xmlns:charts="clr-namespace:Charts;assembly=Charts"

These URI’s do not resolve to any useful resource if you put them in the address bar in Internet Explorer for example, their only job is to be unique.  A common way to guarantee that uniqueness throughout the XAML’s travels is to start by using a domain name previously registered with an Internet Naming Authority such as schemas.microsoft.com and add a custom suffix such as netfx/2007/xaml/presentation.

The Default Namespace

There can be only one default XML namespace for an element, any additional namespaces must use a prefix.  In the default XAML markup below, the URI specified by the xmlns identifier without any prefix is the default namespace for the UserControl element and it’s descendant elements (the namespace’s scope) but default namespaces have no affect on attributes.  This means that all unqualified element names within the default namespace’s scope (UserControl and Grid) are implicitly associated with that namespace’s URI.

<UserControl x:Class="SilverlightApplication1.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>

This is important because it is this implicit namespace URI that the XAML processor uses to search referenced assemblies to find the matching CLR Type for these unqualified elements.  The image below shows the default references for a standard Silverlight Application containing the above markup :

Default References

These referenced assemblies are searched by the XAML processor for a predefined, hardcoded attribute (the XmlnsDefinition attribute) defining this implicit URI as it’s ‘key’ :

System.Windows Assembly Attributes

When found, as many are in the System.Windows.dll assembly only, the name of a CLR namespace is retrieved from the ‘value’ of the same attribute.  This CLR namespace is then returned as a scope for the XAML processor to search within to find the matching CLR Type for the XAML element.  Both the UserControl and the Grid types are found in the System.Windows.Controls CLR namespace in this way :

UserControl Grid

You can see from the illustration above that twelve CLR namespaces in the System.Windows.dll assembly are mapped to the Silverlight Beta 2 default XAML namespace and that the Beta 1 namespace is currently still supported.  Note that this process is also the method by which the CLR Application type, instantiated by the the Silverlight plug-in control, is mapped :

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             x:Class="SilverlightApplication1.App"
             >

</Application>

Using the default XML namespace on the Application element in App.xaml, this fundamental CLR type is found in the System.Windows namespace again in the System.Windows.dll assembly :

Application

Prefixed Namespaces

To recap, there can be only one default XML namespace for an element, any additional namespaces must use a prefix and default namespaces have no affect on attributes.  In order to affect an attribute with a namespace a prefix must be used, the prefix then becomes an abbreviation for that namespace.  In this way the x prefix is used by convention with it’s corresponding pre-defined URI to fulfil Silverlight’s contract as an implementation of the XAML language :

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

The XAML language expects implementers of it’s syntax to expose certain fundamental attributes such as x:Class and x:Name (as show in the examples above) so the processor can perform duties such as joining a code-behind file to a XAML file through a partial class and creating members of that class with meaningful names.  This functionality is manifest in the System.Windows.Markup namespace also in the System.Windows.dll assembly.

For more information on XML namespaces in general and how they affect the family of XML technologies, have a look here and here.

Posted in Silverlight, WPF, XAML | Tagged: , , , , | Comments Off on Beta 2 Xaml Namespaces

Samples Updated to Beta 2

Posted by petermcg on June 10, 2008

The API and documentation changes and the fact that Beta2 will not run applications that target Beta1 has obliged me to update links and all the samples previously posted on this blog such as the Pie Chart and Stock List Demo.  From this point on all posts will target Silverlight 2 Beta 2.

I started with a brand new Beta 2 solution when updating each sample to keep things clean.  The changes required to make the the new Pie Chart Solution work included updating the parameter list for the calls to the static Register method for the Dependency properties in the PieChart class now they require an instance of the PropertyMetadata class; a similar change to the Storyboard.SetTargetProperty method calls in PieLayoutManager that now require an instance of the PropertyPath class.  The need to add the MouseEnter animation Storyboard to the resources of the PieChart Canvas is removed thanks to the new support for the ability to create Storyboards in code that can animate parts of the render tree without having to be added to it.

The Stock List Demo Part 3 sample required the same changes for Storyboard.SetTargetProperty and also benefited from the fact that Storyboards created in code no longer have to be added to the Resources of a FrameworkElement for the stock highlight animations to work.  Other changes I ran into included that EventHandler<T> now requires T to inherit from EventArgs, the DataGrid’s AutoGenerateColumns is now true by default, displays itself using a smaller font by default and supports auto-sized columns.

The biggest change to the Stock List Demo project for Beta 2 was the new policy requirements around creating a Sockets server which have been implemented in the updated sample :

Console.WriteLine("Accepting Policy Requests...");

// Read policy file
byte[] policyBytes = File.ReadAllBytes("PolicyResponse.xml");

using (Socket client = policyListener.AcceptSocket())
{
    Console.WriteLine("Policy Request Accepted...");

    // Get policy request header
    byte[] buffer = new byte[1024];
    int bytesReceived = client.Receive(buffer);

    // Basic check of request header
    string header = Encoding.UTF8.GetString(buffer, 0, bytesReceived);

    if (header == "<policy-file-request/>")
    {
        client.Send(policyBytes, 0, policyBytes.Length, SocketFlags.None);
    }
}

Please download the latest version for a substantial demonstration of many areas of Silverlight development including Socket interaction that complies with the new policy changes in Silverlight 2 Beta 2.

As part of the upgrade I did run into this issue of not being able to discover a WCF web-service in the same solution after installing Beta 2 when creating the new solution for Stock List Demo 3.  The workaround at the end of the thread here solved the problem.

Please feel free to download the updated samples and as usual let me know any thoughts by adding a quick comment.

Posted in Pie Chart, Silverlight, Sockets, WCF | Tagged: , | 3 Comments »

Silverlight 2 Beta 2

Posted by petermcg on June 10, 2008

After a Beta 1 public release that lasted approx three months, Silverlight 2 has now moved on to Beta 2.  This is the first release of Silverlight 2 that has a commercial go-live license, removing another potential obstacle in the decision to adopt the technology for new development.

Plenty of blogs have an overview of the changes here, here, here and here for a start!  Some highlights that I will pick out are that we can ultimately expect over 100 controls for Silverlight, all of which will have built-in support for Control Templates and Visual State Manager customisation.

The Visual State Manager (VSM) is a way to visually manipulate the ‘Parts Model’ of a control and is supported by Expression Blend 2.5 June 2008 Preview.  This is the different visual states and the transition between those states a control allows a designer to modify or completely customize with the guarantee that the logic of the control will not be adversely affected.  The VSM is a feature new to Silverlight 2 that is not currently in WPF; in order to maintain upwards compatibility between the two, WPF will be getting the VSM later this year more than likely with .NET 3.5 SP1.

An ongoing area of particular interest to me (and this blog) will be the the Networking Improvements, the support for Duplex Communication (Server Push) has made it into Beta 2 with the System.ServiceModel.PollingDuplex.dll assembly amongst others.  Two copies of this assembly come with the Beta 2 SDK, one copy under the %ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Client folder and another under the uncharacteristically named ‘Evaluation’ folder at %ProgramFiles%\Microsoft SDKs\Silverlight\v2.0\Libraries\Server, so maybe this support only just made it in…

Posted in Silverlight | Tagged: , , | 3 Comments »

Silverlight 2.0 Stock List Demo – Part 3

Posted by petermcg on June 5, 2008

Edited on 10/June/2008 : Code download updated to Silverlight 2 Beta 2

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

Part Three : Making the Grid Tick with Server Deltas via Sockets – (Code download)

In Part 2 we had a DataGrid control being populated with 1,000 stocks via a ‘state of the world’ response from a call to our WcfStockService web service. To progress with our sample financial stock ticker application the next area to look at is updating the bid and ask prices, to do this we could call the web-service at a specified interval, but this is not ideal. What we really want is not to have to ‘pull’ updates from the server but have the server ‘push’ updates to listening clients.

More advanced duplex communication and ‘push’ support is possibly arriving in the imminent release of Silverlight 2 Beta 2; eventually along with the full cross-domain model for Sockets but in the interim there is basic support for duplex connectivity with the application’s host of origin in Silverlight 2 Beta 1 provided by the System.Net.Sockets.Socket class. This article shows how to use this networking support in Silverlight 2 Beta 1 to send updates and additions (deltas) to one or many Stock List clients from an implementation of a Socket based Stock delta server.

Here’s what our Visual Studio 2008 solution looks like for Part 3 :

Solution Explorer Part 3

The most notable changes in this image since Part 2 are the addition of the new StockDeltaService Console Application project and the StockDecorator class in the Silverlight Application project.

The Server

The new StockDeltaService Console Application project contains the implementation of the Stock delta server. Currently, the only supported protocol for Socket data transfer in Silverlight 2.0 is the TCP protocol so the server uses the standard TcpListener class to listen for clients on an agreed endpoint. Once accepted, a reference to the communication stream back to the client is stored in a thread-safe collection of type SynchronizedCollection<StockClient> :

public void Start()
{
    // Start worker thread
    Thread workerThread = new Thread(ServeDeltas) { IsBackground = true };
    workerThread.Start();

    // Listen for clients
    listener.Start();
    Console.WriteLine("Server started...");

    while (true)
    {
        Console.WriteLine("Accepting clients...");

        // Main Thread's only duty is to block here listening for clients
        StockClient client = new StockClient(listener.AcceptTcpClient());

        Console.WriteLine("Client accepted...");

        // Don't block the Main Thread in Add
        ThreadPool.QueueUserWorkItem(o => synchClients.Add(o as StockClient), client);
    }
}

The main server thread’s only duty is to listen for and accept connections from new clients, a manual background thread does the actual work of generating sample prices and sending these deltas to the previously registered connected clients. The server has a reference to the same WCF web service used in Part 2 and it is the simple Stock type that is sent one at a time in serialized form to the connected clients in the collection. Each Stock object is serialized on the server and eventually deserialized on the client using the XmlSerializer class, the same class that ASP.NET uses to encode XML Web service messages.

By default the server in the code download sample sends deltas every 50 milliseconds, I have had the code send deltas for 1,000 stocks to multiple clients every 5 milliseconds which works fine on my computer in Release mode (Start Without Debugging). Download the code above for more detailed information.

The Client

On the client side, in order to facilitate communication via Sockets with the server the StockTicker class now exposes the SubscribeDeltas() method :

public void SubscribeDeltas()
{
    // Creates a Socket that can be used to communicate on a TCP/IP-based network
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // NOTE : It is advantageous to retain the reference to the SocketAsyncEventArgs object
    SocketAsyncEventArgs args = new SocketAsyncEventArgs();
    args.UserToken = socket;
    args.RemoteEndPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4502);
    args.Completed += OnSocketAsyncEventCompleted;

    // Asynchronously connect to server and process response in OnSocketAsyncEventCompleted
    socket.ConnectAsync(args);
}

private void OnSocketAsyncEventCompleted(object sender, SocketAsyncEventArgs e)
{
    Socket socket = e.UserToken as Socket;

    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Connect:
            // Set up receive buffer
            byte[] buffer = new byte[1024];
            e.SetBuffer(buffer, 0, buffer.Length);
            // Asynchronously request to receive data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.None:
            break;
        case SocketAsyncOperation.Receive:
            // Deal with received data from the server in our buffer
            string decoded = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);

            using (StringReader sreader = new StringReader(decoded))
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(Stock));

                try
                {
                    // Deserialize single Stock object
                    Stock stock = xmlSerializer.Deserialize(sreader) as Stock;

                    if (stock != null)
                    {
                        // Queue call to UpdateStockList method passing delta parameter on the thread that created StockTicker
                        Action<Stock> action = UpdateStockList;
                        owner.Dispatcher.BeginInvoke(action, stock);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

            // Asynchronously request to receive more data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.Send:
            break;
        default:
            break;
    }
}

This public method is called on the same instance of the StockTicker from Part 2 in the constructor of Page.xaml.cs just below our previous call for the state of the world data. The SubscribeDeltas method firstly creates a Socket that can be used to communicate on a TCP/IP-based network (such as the Internet) and uses it along with a new instance of the SocketAsyncEventArgs class to start an asynchronous connection request to the server with the Completed event handled by the OnSocketAsyncEventCompleted method. Note that this sample uses port 4502, one restriction in Silverlight 2 Beta 1 is that the port range that a network application is allowed to connect to must be within the range 4502-4532.

Once connected with the server, the client waits to asynchronously receive the updates it has subscribed to. When deltas are received the Stock object is deserialized and the StockList collection is updated via the UpdateStockList method on the thread that created the StockTicker class. Queuing the call to UpdateStockList for execution on the creating thread using Dispatcher.BeginInvoke is necessary to prevent an invalid cross-thread access exception when a collection (StockList in this case) is updated from a different thread than the one that created it.

Changes to the client presentation in Page.xaml are minimal and are focused around binding to the highlighting animation that takes place when a price for a Stock changes :

<data:DataGrid x:Name="StocksGrid"
    Grid.Row="1"
    Grid.ColumnSpan="2"
    GridlinesVisibility="Horizontal"
    HeadersVisibility="Column"
    RowBackground="AliceBlue"
    IsReadOnly="True"
    CanUserResizeColumns="False"
    Margin="0,5,0,0">
    <data:DataGrid.Columns>
        <data:DataGridTemplateColumn Header="Symbol">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Symbol}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Bid">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding BidHighlight}">
                        <TextBlock Text="{Binding Bid}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Ask">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding AskHighlight}">
                        <TextBlock Text="{Binding Ask}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn MinWidth="150" Header="Notes">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Notes}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

A StackPanel now wraps the display of each price with it’s background color bound to the appropriate System.Windows.Media.Brush exposed by the new StockDecorator class. The fading green animation for a positive change in stock price or red for negative is achieved by animating the color of that Brush using a ColorAnimationUsingKeyFrames object added to a Storyboard. A simple Notes field for each Stock has also been added to demonstrate how a user could modify the currently selected Stock in the DataGrid using UI controls.

A screenshot of the sample application running with multiple clients connected using Firefox or Internet Explorer is shown below :

Stock Delta Server and Connected Stock Clients

Until now this type of application has typically been implemented using Ajax/Flex and the Comet Messaging Protocol with offerings in this space from applications such as Lightstreamer and Liberator. This is only a sample but it’s clear to see Silverlight already provides another option for this type of application and with the updates hopefully coming in Beta 2 and beyond in this area such as DuplexReceiver<T> the best is theoretically yet to come.

At this stage we have a substantial sample application that demonstrates how to use a variety of powerful features of Silverlight 2.0 Beta 1 for creating Rich Internet Applications (RIA) applications. This post has provided an overview of how the solution fits together but to get the most out of this sample download the full source code and explore.

Posted in Silverlight, Sockets, WCF | Tagged: , , , , , , , | 2 Comments »

Silverlight 2.0 Unhandled Exceptions

Posted by petermcg on June 3, 2008

It can be difficult to debug exceptions in a Silverlight applications in Visual Studio 2008 in certain scenarios at present.  If you’re having trouble tracking down where an exception is occurring in your Silverlight application and find it awkward dealing with the yellow exclamation :

Yellow Exclamation

Don’t forget you’ve got the Application events to help you out.  An event handler for the UnhandledException event of the Application class is wired up by default for new Silverlight Applications.  If you set a breakpoint here you can get a call stack back to where the problem is occurring :

Application UnhandledException

Posted in Debugging, Silverlight, Visual Studio 2008 | Tagged: , | Comments Off on Silverlight 2.0 Unhandled Exceptions