Contents tagged with Sockets

  • Creating a Silverlight 2 Client Access Policy Socket Server

    Silverlight 2 provides built-in support for sockets which allows servers to push data to Silverlight clients.  By using this feature clients can avoid polling the server on a timed basis to ensure that clients are kept up-to-date.  If you're new to the socket features built-into Silverlight 2 you'll want to read my previous posts to get additional details about how data can be pushed from a server to a client:

    Silverlight 2 Beta 2 (and beyond) checks for a client access policy before accessing sockets located on host domain or cross-domain servers.  An example of a client access policy for sockets is shown next:

    <?xml version="1.0" encoding ="utf-8"?>
    <access-policy>
      <cross-domain-access>
        <policy>
          <allow-from>
            <domain uri="*" />
          </allow-from>
          <grant-to>
            <socket-resource port="4530" protocol="tcp" />
          </grant-to>
        </policy>
      </cross-domain-access>
    </access-policy>

    This XML code allows Silverlight to access a TCP socket on port 4530.  A range of ports can be specified in the port attribute if needed (ex: 4530-4532).  Before Silverlight tries to call a server with a socket, it makes a call to the target server on port 943 to check the client access policy and see if the server allows socket connections.  This helps minimize various types of hacker attacks.  If a client access policy is available on the server and the policy allows access to the port the client is trying to call, processing of the socket code continues and Silverlight tries to connect.  If not, the client will be unable to connect due to access being denied by Silverlight.

    An example of creating a client access policy socket server that Silverlight can connect to on port 943 is shown next:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    using System.Threading;
    using System.Reflection;
    using System.Configuration;
    
    namespace PolicySocketServices
    {
        class PolicySocketServer
        {
            TcpListener _Listener = null;
            TcpClient _Client = null;
            static ManualResetEvent _TcpClientConnected = new ManualResetEvent(false);
            const string _PolicyRequestString = "<policy-file-request/>";
            int _ReceivedLength = 0;
            byte[] _Policy = null;
            byte[] _ReceiveBuffer = null;
    
            private void InitializeData()
            {
                string policyFile = ConfigurationManager.AppSettings["PolicyFilePath"];
                using (FileStream fs = new FileStream(policyFile, FileMode.Open))
                {
                    _Policy = new byte[fs.Length];
                    fs.Read(_Policy, 0, _Policy.Length);
                }
                _ReceiveBuffer = new byte[_PolicyRequestString.Length];
            }
    
            public void StartSocketServer()
            {
                InitializeData();
    
                try
                {
                    //Using TcpListener which is a wrapper around a Socket
                    //Allowed port is 943 for Silverlight sockets policy data
                    _Listener = new TcpListener(IPAddress.Any, 943);
                    _Listener.Start();
                    Console.WriteLine("Policy server listening...");
                    while (true)
                    {
                        _TcpClientConnected.Reset();
                        Console.WriteLine("Waiting for client connection...");
                        _Listener.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
                        _TcpClientConnected.WaitOne(); //Block until client connects
                    }
                }
                catch (Exception exp)
                {
                    LogError(exp);
                }
            }
    
            private void OnBeginAccept(IAsyncResult ar)
            {
                _Client = _Listener.EndAcceptTcpClient(ar);
                _Client.Client.BeginReceive(_ReceiveBuffer, 0, _PolicyRequestString.Length, SocketFlags.None,
                    new AsyncCallback(OnReceiveComplete), null);
            }
    
            private void OnReceiveComplete(IAsyncResult ar)
            {
                try
                {
                    _ReceivedLength += _Client.Client.EndReceive(ar);
                    //See if there's more data that we need to grab
                    if (_ReceivedLength < _PolicyRequestString.Length)
                    {
                        //Need to grab more data so receive remaining data
                        _Client.Client.BeginReceive(_ReceiveBuffer, _ReceivedLength, 
                            _PolicyRequestString.Length - _ReceivedLength,
                            SocketFlags.None, new AsyncCallback(OnReceiveComplete), null);
                        return;
                    }
    
                    //Check that <policy-file-request/> was sent from client
                    string request = System.Text.Encoding.UTF8.GetString(_ReceiveBuffer, 0, _ReceivedLength);
                    if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _PolicyRequestString) != 0)
                    {
                        //Data received isn't valid so close
                        _Client.Client.Close();
                        return;
                    }
                    //Valid request received....send policy data
                    _Client.Client.BeginSend(_Policy, 0, _Policy.Length, SocketFlags.None, 
                        new AsyncCallback(OnSendComplete), null);
                }
                catch (Exception exp)
                {
                    _Client.Client.Close();
                    LogError(exp);
                }
                _ReceivedLength = 0;
                _TcpClientConnected.Set(); //Allow waiting thread to proceed
            }
    
            private void OnSendComplete(IAsyncResult ar)
            {
                try
                {
                    _Client.Client.EndSendFile(ar);
                }
                catch (Exception exp)
                {
                    LogError(exp);
                }
                finally            
                {
                    //Close client socket
                    _Client.Client.Close();
                } 
            }
    
            private void LogError(Exception exp)
            {
                string appFullPath = Assembly.GetCallingAssembly().Location;
                string logPath = appFullPath.Substring(0, appFullPath.LastIndexOf("\\")) + ".log";
                StreamWriter writer = new StreamWriter(logPath, true);
                try
                {
                    writer.WriteLine(logPath,
                        String.Format("Error in PolicySocketServer: "
                        + "{0} \r\n StackTrace: {1}", exp.Message, exp.StackTrace));
                }
                catch { }
                finally
                {
                    writer.Close();
                }
            }
        }
    }

    Looking through the code you'll see that it uses the TcpListener class to listen for incoming client connections.  Once a client connects the code checks the request for the following value:

    <policy-file-request/>

    Silverlight automatically sends this text to the policy file socket once it connects.  If the request contains the proper value the code writes the contents of the client access policy back to the client stream (see the OnReceiveComplete() method).  Once the policy file is received, Silverlight parses it, checks that it allows access to the desired port, and then accepts or rejects the socket call that the application is trying to make. 

    An example of the Silverlight GameStream socket application I created to demonstrate the fundamentals of using sockets is shown next.  The code for the application can be downloaded here

    If you're brand new to Silverlight 2 and are interested in getting started with it check out the following video tutorials:

    Silverlight 2.0 Video Tutorials

    Part 1: Creating "Hello World" with Silverlight 2 and VS 2008 Tutorial Video Tutorial
    Part 2: Using Layout Management Tutorial Video Tutorial
    Part 3: Using Networking to Retrieve Data and Populate a DataGrid Tutorial Video Tutorial
    Part 4: Using Style Elements to Better Encapsulate Look and Feel Tutorial Video Tutorial
    Part 5: Using the ListBox and DataBinding to Display List Data Tutorial Video Tutorial
    Part 6: Using User Controls to Implement Master/Details Scenarios Tutorial Video Tutorial
    Part 7: Using Templates to Customize Control Look and Feel Tutorial Video Tutorial
    Part 8: Creating a Digg Desktop Version of our Application using WPF Tutorial Video Tutorial
     

     

    Read more...

  • Pushing Data to a Silverlight Client with Sockets: Part II

    In Part 1 of this two part series on socket support in Silverlight 2 I discussed how a server could be created to listen for clients using classes in the System.Net.Sockets namespace.  In that post the TcpListener class was used to listen for client connections and the client stream was accessed using the TcpClient class's GetStream() method.  In this post I'll cover how a Silverlight client can connect to a server using sockets and receive data pushed by the server asynchronously.

    Creating a Silverlight Socket Client

    Silverlight 2 provides a Socket class located in the System.Net.Sockets namespace (a Silverlight namespace...not the .NET Framework namespace mentioned earlier) that can be used to connect a client to a server to send and/or receive data.  The Socket class works in conjunction with a SocketAsyncEventArgs class to connect and send/receive data.  The SocketAsyncEventArgs class is used to define asynchronous callback methods and pass user state between those methods (such as the Socket object that originally connected to the server).  An example of using the Socket and SocketAsyncEventArgs classes is shown next:

    private void Page_Loaded(object sender, RoutedEventArgs e)
    {
        DnsEndPoint endPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4530);
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.UserToken = socket;
        args.RemoteEndPoint = endPoint;
        args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
        socket.ConnectAsync(args);   
    }


    This code first identifies the application's host server along with a port of 4530 (Silverlight can only connect to servers on a port between 4502 and 4532).  A Socket object capable of communicating using the TCP protocol is then created.  Once the Socket object is created, a SocketAsyncEventArgs object is instantiated and the socket object is assigned to the UserToken property so that it is passed through to other methods.  The target endpoint of the server is set using the RemoteEndPoint property and the argument object's Completed event is hooked to an asynchronous callback method named OnSocketConnectCompleted.  Once these objects are created and ready to use, the Socket object's ConnectAsync() method can be called which accepts a SocketAsyncEventArgs object as a parameter.

    When the client is connected to the server the following method is called which creates a buffer to hold data received from the server and rewires the SocketAsyncEventArgs's Completed event to a different callback method named OnSocketReceive:

    private void OnSocketConnectCompleted(object sender, SocketAsyncEventArgs e)
    {
        byte[] response = new byte[1024];
        e.SetBuffer(response, 0, response.Length);
        e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
        e.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketReceive);
        Socket socket = (Socket)e.UserToken;
        socket.ReceiveAsync(e);
    }


    The Socket object's ReceiveAsync() method is then called to begin the process of accepting data from the server.  As data is received the OnSocketReceive() method is called which handles deserializing XML data returned from the server into CLR objects that can be used in the Silverlight client. 

    private void OnSocketReceive(object sender, SocketAsyncEventArgs e)
    {
        StringReader sr = null;
        try
        {
            string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
            sr = new StringReader(data);
            //Get initial team data
            if (_Teams == null && data.Contains("Teams"))
            {
                XmlSerializer xs = new XmlSerializer(typeof(Teams));
                _Teams = (Teams)xs.Deserialize(sr);
                this.Dispatcher.BeginInvoke(UpdateBoard);
            }
    
            //Get updated score data
            if (data.Contains("ScoreData"))
            {
                XmlSerializer xs = new XmlSerializer(typeof(ScoreData));
                ScoreData scoreData = (ScoreData)xs.Deserialize(sr);
                ScoreDataHandler handler = new ScoreDataHandler(UpdateScoreData);
                this.Dispatcher.BeginInvoke(handler, new object[] { scoreData });
            }
        }
        catch { }
        finally
        {
            if (sr != null) sr.Close();
        }
        //Prepare to receive more data
        Socket socket = (Socket)e.UserToken;
        socket.ReceiveAsync(e);
    }


    The deserialization process is handled by the XmlSerializer class which keeps the code nice and clean.  Alternatively, the XmlReader class could be used or the LINQ to XML technology that's available in Silverlight.  Notice that data is routed back to the UI thread by using the Dispatcher class's BeginInvoke() method.  If you've worked with Windows Forms before then this should look somewhat familiar.  That's all there is to work with sockets in a Silverlight client.  There's certainly some work involved, but many of the complexities are hidden away in the Socket and SocketAsyncEventArgs classes which both minimize the need to work with threads directly.  I've already shown what the client interface looks like as data is updated but here it is one more time (click the image to see an animated gif of the application in action):

    The complete code for the Silverlight client and server application can be downloaded here.

    Read more...

  • Pushing Data to a Silverlight Client with Sockets: Part I

    Silverlight 2 has built-in support for sockets which creates some interesting possibilities.  If you've ever worked on a client-side application that needed to receive up-to-date data then you're probably used to solving the problem by polling.  With polling the client contacts the server on a consistent, timed basis to see if any updates are available.  ASP.NET AJAX provides a Timer control that makes this process easy and straightforward.

    Read more...

comments powered by Disqus