Page view counter

Silverlight Tips of the Day - Blog by Mike Snow

Game Programming with Silverlight

Silverlight Tip of the Day #12 - Full Implementation of a Silverlight Policy Server.

Before a Silverlight application can connect to a server it must first successfully connect to a policy server on that machine in order to proceed with the connection.

In this tip I will take you through every step you need to create and run your own policy server.

To start, create a new C# console application.  Then, create a new XML file called “clientaccesspolicy.xml” and add it to your project. This is the file your Policy Server will send to a client giving it permission to proceed with the connection.

The contents of the “clientaccesspolicy.xml” are as follows:

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

Note that with Silverlight, sockets are limited to ports #4502-4534.  Also, the policy server that sends this policy file to the connecting client must be run on port 943.

The code for the policy server itself is as follows:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
 
namespace PolicyServer
{
    // Encapsulate and manage state for a single connection from a client
    class PolicyConnection
    {
        private Socket _connection;
        private byte[] _buffer; // buffer to receive the request from the client       
        private int _received;
        private byte[] _policy; // the policy to return to the client
 
        // the request that we're expecting from the client
        private static string _policyRequestString = "<policy-file-request/>";
 
        public PolicyConnection(Socket client, byte[] policy)
        {
            _connection = client;
            _policy = policy;
 
            _buffer = new byte[_policyRequestString.Length];
            _received = 0;
 
            try
            {
                // receive the request from the client
                _connection.BeginReceive(_buffer, 0, _policyRequestString.Length, SocketFlags.None,
                    new AsyncCallback(OnReceive), null);
            }
            catch (SocketException)
            {
                _connection.Close();
            }
        }
 
        // Called when we receive data from the client
        private void OnReceive(IAsyncResult res)
        {
            try
            {
                _received += _connection.EndReceive(res);
 
                // if we haven't gotten enough for a full request yet, receive again
                if (_received < _policyRequestString.Length)
                {
                    _connection.BeginReceive(_buffer, _received, _policyRequestString.Length - _received,
                        SocketFlags.None, new AsyncCallback(OnReceive), null);
                    return;
                }
 
                // make sure the request is valid
                string request = System.Text.Encoding.UTF8.GetString(_buffer, 0, _received);
                if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _policyRequestString) != 0)
                {
                    _connection.Close();
                    return;
                }
 
                // send the policy
                Console.Write("Sending policy...\n");
                _connection.BeginSend(_policy, 0, _policy.Length, SocketFlags.None,
                    new AsyncCallback(OnSend), null);
            }
            catch (SocketException)
            {
                _connection.Close();
            }
        }
 
        // called after sending the policy to the client; close the connection.
        public void OnSend(IAsyncResult res)
        {
            try
            {
                _connection.EndSend(res);
            }
            finally
            {
                _connection.Close();
            }
        }
    }
 
    // Listens for connections on port 943 and dispatches requests to a PolicyConnection
    class PolicyServer
    {
        private Socket _listener;
        private byte[] _policy;
 
        // pass in the path of an XML file containing the socket policy
        public PolicyServer(string policyFile)
        {
 
            // Load the policy file
            FileStream policyStream = new FileStream(policyFile, FileMode.Open);
 
            _policy = new byte[policyStream.Length];
            policyStream.Read(_policy, 0, _policy.Length);
            policyStream.Close();
 
 
            // Create the Listening Socket
            _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                ProtocolType.Tcp);
 
            _listener.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)
                SocketOptionName.NoDelay, 0);
 
            _listener.Bind(new IPEndPoint(IPAddress.Any, 943));
            _listener.Listen(10);
 
            _listener.BeginAccept(new AsyncCallback(OnConnection), null);
        }
 
        // Called when we receive a connection from a client
        public void OnConnection(IAsyncResult res)
        {
            Socket client = null;
 
            try
            {
                client = _listener.EndAccept(res);
            }
            catch (SocketException)
            {
                return;
            }
 
            // handle this policy request with a PolicyConnection
            PolicyConnection pc = new PolicyConnection(client, _policy);
 
            // look for more connections
            _listener.BeginAccept(new AsyncCallback(OnConnection), null);
        }
 
        public void Close()
        {
            _listener.Close();
        }
    }
 
    public class Program
    {
        static void Main()
        {
            Console.Write("Starting...\n");
            PolicyServer ps =
                new PolicyServer(@"<path to your file>\clientaccesspolicy.xml");
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
        }
    }
}

You will need to change where it says "<path to your file>\clientaccesspolicy.xml" to be the actual path to where you saved this file on your server.

Thank you,
--Mike Snow

 Subscribe in a reader

Comments

Microsoft Weblogs said:

In Silverlight Beta 1 you could only use sockets to connect to the same host the Silverlight application

# June 26, 2008 7:15 PM

Community Blogs said:

Jaime Rodriguez on DeepZoom, Pete Brown on on Publish/Subscribe pattern in SL, Shawn Wildermuth on XAML

# June 27, 2008 6:12 PM

Silverlight news for June 30, 2008 said:

Pingback from  Silverlight news for June 30, 2008

# June 30, 2008 3:41 AM

chrisaswain said:

This saved me a lot of trouble.  Thanks!

# July 1, 2008 11:14 AM

Silverlight Tips of the Day - Blog by Mike Snow said:

The purpose of this post is to create an outline summary all the blogs from my Silverlight tips of the

# January 2, 2009 5:57 PM

o UAU nosso de cada dia said:

essa lista eu copiei desse blog bárbaro (acompanhe por RSS você também): uma lista de dicas super úteis

# January 3, 2009 6:25 AM

VyvIT said:

Hi,

I have some trouble with this implementation and I don't know how to debug it. Maybe you could help?

Problem: After starting this policy server, CPU usage goes up to 90%.

To reproduce the problem follow these steps:

1. start the server

2. connect and disconnect quickly a 2-3 times to this server without getting the policy file and the CPU will go up as I mentioned above.

As a TCP client for testing I use Hercules (www.hw-group.com/.../index_en.html) to connect to the policy server (TCP Client tab).

# January 9, 2009 9:54 AM

mike.snow said:

Does it stay stuck at 90%?

Also, does it happen if you do not "quickly" connect and disconnect?

Thanks.

# January 9, 2009 1:01 PM

VyvIT said:

yes, it stays at 90%

what I figured out is that if i connect and disconnect for the first time, cpu stays on ~50% and if i do it one more time, cpu goes up to 90% and stays stuck.

F.e.:

1. CPU 0%

2. I connect to the server and disconnect (CPU 50%)

3. I connect to the server and disconnect (CPU 90%)

But if:

1. CPU 0%

2. I connect to the server, get the policy file and disconnect (CPU 0%)

...

n. I connect to the server, get the policy file and disconnect (CPU 0%)

So, if one client looses connection without getting the policyfile, my server CPU goes up to 50%, and if connection is lost one more time, my CPU is always on 90%.

# January 12, 2009 9:12 AM

mike.snow said:

I have forward your comments on to the SL team and we'll see if we can get a repro. I'll let you know what we find.

Thanks!

# January 12, 2009 1:46 PM

Community Blogs said:

1. Introduction Currently Silverlight supports two ways of duplex communication: sockets and WCF polling

# May 28, 2009 6:49 AM

goberman said:

Yes, there is a bug in this code: OnReceive should check for 0 _received. 0 indicates that socket is closed. Something like below:

// socket closed

if (_received == 0)

{

 _connection.Close();

 return;

}

It is a shame Microsoft cannot just package a policy server implementation in the Silverlight installation as a tool and has to use these blogs to show how to write one...

And what is the deal with '_' before field names? Some new naming convention?

# July 2, 2009 12:02 PM

mike.snow said:

Yes, a naming convention we use for class variables.

Thanks for the bug report.

# July 13, 2009 6:07 PM

avtar said:

How to get port number from clientaccespolciy in silverlight code?

# September 25, 2009 5:22 AM

nccsbim071 said:

Hi

Mr Mikw Snow

I want to use sockets to develop an online game, where I expect to create a socket server and number of client will connect to the server and get data from the server.

I do not want to use polling.

But as i search the web i find using socket server is not a solution, since Firewall, proxies block the port to communicate with the client and server sockets. And the other problem is i want to host the web on the internet on some asp.net web hosting provider. How am i supposed to start the server on the web host server.

If there are so many problems with the socket server, why is there a socket server available in .net.

What will be my feasible solution.

Please suggest what to do.

Thanks

# October 9, 2009 6:54 AM