views:

1397

answers:

2

How do I go about making a socket policy file server in C#. All it has to do is listen on port 843 for the string "<policy-file-request/>" followed by a NULL byte and then return an XML string (which is the socket policy file).

I haven't coded this sort of thing before and am unsure of where to start. Do I create it in a windows service? Any tips or links are welcome.

Background:

To contact a web service from flash I am using the 'as3httpclient' library instead of the URLRequest/URLLoader. This is because it gives me the ability to send custom headers with GET requests. This library uses low-level sockets to do its stuff.

When flash uses low-level sockets to connect to a server it looks for a socket policy file - and this needs to be served up by a socket policy file server.

Socket Policy File Article from Adobe

+1  A: 

A few things to be aware of using your suggested architecture:

http://stackoverflow.com/questions/285290/trying-to-send-an-http-request-over-sockets

Principally, you need to be aware that even though you can chat http at a lower level using sockets, there are a large number of cases where communication in this fashion will fail. Mainly these failures will occur if the user has a proxy server enabled in their browser, as there is no effective means of discovering and subsequently using the proxy when connecting via a socket.

In order to make a policy server, you can use the TcpListener class. You would start listening as follows:

var tcpListener = new TcpListener(IPAddress.Any, 843 );
tcpListener.start();
tcpListener.BeginAcceptTcpClient(new AsyncCallback(NewClientHandler), null);

The method NewClientHandler would have the form:

    private void NewClientHandler(IAsyncResult ar)
    {
        TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar);
        ...

At which point you might want to supply the tcpClient object to a class of your own creation to handle the validation of the data coming from the socket. I'm going to call it RemoteClient.

In RemoteClient, you'd have something like this:

    var buffer=new byte[BUFFER_SIZE];
    tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, Receive, null);

and a Receive method:

    private void Receive(IAsyncResult ar)
    {
        int bytesRead;
        try
        {
            bytesRead = tcpClient.GetStream().EndRead(ar);
        }
        catch (Exception e)
        {

            //something bad happened. Cleanup required
            return;
        }

        if (bytesRead != 0)
        {
            char[] charBuffer = utf8Encoding.GetChars(buffer, 0, bytesRead);
            try
            {
                tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, Receive, null);
            }
            catch (Exception e)
            {
                //something bad happened. Cleanup required
            }
        }
        else
        {
            //socket closed, I think?
            return;
        }
    }

and some send methods:

    public void Send(XmlDocument doc)
    {
        Send(doc.OuterXml);
    }
    private void Send(String str)
    {

        Byte[] sendBuf = utf8Encoding.GetBytes(str);
        Send(sendBuf);
    }
    private void Send(Byte[] sendBuf)
    {
        try
        {
            tcpClient.GetStream().Write(sendBuf, 0, sendBuf.Length);
            tcpClient.GetStream().WriteByte(0);
            tcpClient.GetStream().WriteByte(13); //very important to terminate XmlSocket data in this way, otherwise Flash can't read it.

        }
        catch (Exception e)
        {
            //something bad happened. cleanup?
            return;
        }
    }

That's all the important details I think. I wrote this some time ago... the Receive method looks like it could do with a rework, but it should be enough to get you started.

spender
Great - thanks for the headsup on the proxy problem as well.
dan
Just to be clear - the link to the SO question is about appending an extra /r/n. That hasn't got anything to do with the proxy problem you mention in the next paragraph does it ? It's two different issues to be aware of?
dan
Correct. Proxy issue is different.
spender
The proxy issue is a killer for us - I'm going to change the web services to POST and use the standard URLRequest/URL Loader - I believe we can add headers that way.
dan
That's right. You can add headers to a POST, but not to a GET due to host browser limitations (although this is very poorly documented). I think I got to around the same point as you have reached when I realised the futility of socket based HTTP.
spender
+1  A: 

Create a listening socket.

When a connection is opened, perform a receive and wait for the expected string. When received send the file content and then close the socket.

Wrap this in a service (running as a low privilege account).

Most of the work is done with the System.Net.Sockets.Socket class, the documentation contains a sample, The API is very similar to the BSD socket API overall (largely there is a 1:1 mapping from BSD API to Socket (or help type) method) so any background should be easily translatable.

Richard
+1 for running in a service as a low privileged account advice. Thanks.
dan