Peter McGrattan’s Weblog

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

Archive for the ‘Sockets’ Category

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 »

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.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 »