How to Add XML Socket Policy Server?
Copy link to clipboard
Copied
So I'm making a TCP Socket Flash game, and I got it connected and working alright, it works fine over the LAN, but when I try to play with someone on a different router using Hamachi, the first message sends fine, but every additional message just throws the "Operation Attempted On Invalid Socket" error and it disconnects them. I'm assuming this is due to the socket policy server. I THOUGHT I added one correctly... but I guess I didn't? This is my code in the AIR server:
var security:URLLoader = new URLLoader();
var securityContent:XML;
security.load(new URLRequest("security-app.xml"));
security.addEventListener(Event.COMPLETE, loadComplete);
function loadComplete(e:Event):void
{
securityContent = XML(security.data);
securityContent.ignoreWhite = true;
security.removeEventListener(Event.COMPLETE, loadComplete);
}
addEventListener(Event.ENTER_FRAME, loadComplete);
And this is the "security-app.xml" file:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
-<cross-domain-policy>
<allow-access-from to-ports="8080" domain="*"/>
</cross-domain-policy>
Is there something I did wrong? I've been looking around allover the internet for days but I can't seem to get an answer. Thanks.
Copy link to clipboard
Copied
Did you note that you're adding an ENTER_FRAME listener that's going to run your loadComplete() function every single frame? It might work a few frames but if you leave the frame where security is defined you will lose that data and remove your socket policy. I'm pretty sure you didn't mean to do that.
You should be waiting for that loadCompete() to run from the socket policy loading, then continue only after it's loaded.
Copy link to clipboard
Copied
Thank you, and actually recently I did change it to that. However, I have a couple questions... when I send the game Win Projector (EXE) over to someone else to play, do I also need to send them that security-app.xml file, or is that already contained in the published file? Also, do I have to paste this whole policy server code in the AIR server or in the client, or both?
Copy link to clipboard
Copied
Your server is responsible for sending the socket policy file per spec. So if you made a custom server, you're responsible for doing this manually. You can hard code the text or read it from an external file, it's all the same when it comes down to it. But you must do it yourself in your server. The policy file is for the server. The client merely reads it when sent to understand it is authorized to deal with that server.
Copy link to clipboard
Copied
Thank you, that clears some things up. So at the part of the code where the messages are sent to the clients, do I also have to send the socket policy file? That is something no one has told me. And, for that XML document I have showed, what would be the equivalent to that if it were just hard coded into the AIR server's script itself? I don't understand why everyone uses XML documents... is it really THAT difficult to just code it into the document itself?
Copy link to clipboard
Copied
Have a look here, especially at the bottom. It explains what the client should send to the server (requesting the policy file) and how you should handle it:
Communicating between Flash Player and Adobe AIR with sockets | Adobe Developer Connection
Copy link to clipboard
Copied
I looked through it and am trying a few things, I am getting an error though, it says
Error: [strict] Ignoring policy file at http://security-app.xml/ due to incorrect syntax.
What is wrong with the policy file? I don't see anything wrong with it...
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
-<cross-domain-policy>
<allow-access-from to-ports="8080" domain="*"/>
</cross-domain-policy>
Copy link to clipboard
Copied
Well, the page did not really make it clear how I'm supposed to add the "handshakeHandler" event. I added it here:
function connectHandler(event:ServerSocketConnectEvent):void
{
var socket:Socket = event.socket as Socket;
clientSockets.push( socket );
socket.addEventListener( ProgressEvent.SOCKET_DATA, handshakeHandler); //<--------------
socket.addEventListener( Event.CLOSE, onClientClose );
socket.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
socket.addEventListener(ProgressEvent.SOCKET_DATA, loadComplete);
log( "A client has successfully connected to the server.");
}
But even then it doesn't work correctly, and the page also didn't really say how it would include the XML document, here's what I used, as it told me to...
function handshakeHandler( event:ProgressEvent ):void
{
var socket:Socket = event.target as Socket;
//Read the message from the socket
var message:String = socket.readUTFBytes( socket.bytesAvailable );
log( "Received: " + message);
if( message == "" )
{
var policy:String = '\x00';
socket.writeUTFBytes( policy );
socket.flush();
socket.close();
log("Sending policy: " + policy);
} else if ( message == "BEGIN" )
{
socket.removeEventListener( ProgressEvent.SOCKET_DATA, handshakeHandler );
socket.addEventListener( ProgressEvent.SOCKET_DATA, socketDataHandler );
socket.writeUTFBytes( "READY" );
socket.flush();
}
}
I don't see how the message would just be nothing. Nothing happens with this code, it never makes it over to the socketDataHandler. How does it implement the XML document? I have it in its own code here...
var security:URLLoader = new URLLoader();
var securityContent:XML;
function loadComplete(e:Event):void
{
security.load(new URLRequest("security-app.xml"));
securityContent = XML(security.data);
securityContent.ignoreWhite = true;
removeEventListener(Event.COMPLETE, loadComplete);
}
addEventListener(ProgressEvent.SOCKET_DATA, loadComplete);
Copy link to clipboard
Copied
Instead of just perusing the code you really should read the article.
You should have learned Flash Player will automatically send a socket policy file request and what exactly it sends to your server (<policy-file-request/>). You're allowed to change which port that request is sent to via the loadPolicyFile method of Security, configured BEFORE a connect() (e.g. Security.loadPolicyFile("xmlsocket://1.2.3.4:8888");). Also your server needs to both be listening on your chosen data port but also listening on the socket policy file port, if you choose them to differ. Once you receive the request, you send your policy file, of which an example is provided, terminated by a null byte (\x00).
If the client does not receive the policy file, it's never going to proceed with connecting to an internet destination.
In the article the developer coded the server to determine what the client wanted by simply reading what it sent. If the var message:String was empty then they considered that it was a socket policy request. You're right, the code for the null byte is there but the socket policy file isn't listed in the code, because every person needs something different. Copy the policy file text into that same policy:String and make sure it's terminated by the null byte and send it all. The article is not 100% literal or complete. You should be expected to identify what the code lacks and compete it, which you will if you read the entire article.
After the policy is sent and connected the server only listens for "BEGIN" and then writes back "READY" to the client. Just a simple demonstration of primitive UTF communication. Now you convert it to do what you need it to do rather than what the author did for the tutorial.
Copy link to clipboard
Copied
Yeah, I did read it.
I got it working to an extent. It does connect properly, however once it reaches the socketDataHandler, it gives me the "operation attempted on invalid socket" error when I try to write a message over. Here is my handshakeHandler code:
function handshakeHandler( event:ProgressEvent ):void
{
var socket:Socket = event.target as Socket;
var message:String = socket.readUTFBytes(socket.bytesAvailable);
var policy:String = '<cross-domain-policy> <allow-access-from domain="*" to-ports="8080" /> </cross-domain-policy>\x00';
socket.writeUTFBytes( policy );
socket.flush();
socket.removeEventListener( ProgressEvent.SOCKET_DATA, handshakeHandler );
socket.addEventListener( ProgressEvent.SOCKET_DATA, socketDataHandler );
}
I'm basically just making it write the policy as soon as a client connects. The error occurs when this script initiates:
function socketDataHandler(event:ProgressEvent):void
{
var socket:Socket = event.target as Socket;
var message:String = socket.readUTFBytes(socket.bytesAvailable);
for each (var socket:Socket in clientSockets)
{
socket.writeUTFBytes(message);
socket.flush();
}
}
I tried putting in "socket.close();" in the handshakeHandler, and though that stopped the error, it didn't allow anything to happen because the socket was closed. Do you know what's going on here?
Copy link to clipboard
Copied
I see you reading data here and the example is incomplete so I have no idea what you're trying to transmit to your (presumably) array of clientSockets.
Let me try to explain things differently.
A Flash clients automatically will try to download a policy file, by default on port 843 (unless you specify another port). If it cannot connect to your specified port (or default 843), it will attempt to use a current socket connection to read the policy. This means you can use the same IP/port that you wish to use for data to also serve the policy file. Now, you must code your server to be listening for "<policy-file-request/>\x00", and then serve back a valid policy file.
Here's where you might be getting confused so let me re-iterate that. On the client side, you do not need to do anything at all regarding receiving the policy file. Flash automates this as soon as you attempt to connect to a socket, behind the scenes. While you might think your socket connection is the first thing that happens, Flash is holding your connection request until it can complete the grabbing of the policy file.
Once the server sends that policy file, the connection must close. If you decided to use the same port for data and for the policy file, you must close the connection, and reconnect.
That said, your Flash client will receive this policy file and as long as it's a valid policy file, the receiving and parsing of it are automatic. Your Flash client will look for the "<cross-domain-policy>" node and read all "allow-access-from" rules. This is automatic, not requiring you to do anything. If Flash decides, based on the policy, you have the right to connect, THAT is when your client suddenly is allowed to connect to the server.
Let me re-iterate that the entire first portion of what I said happens automatically, without you. Again, the Flash client sends it's own request for the policy and handles the response without you. This happens when you attempt to connect to the server, but is completed before that connection ever happens. All of what I mentioned happens automatic.
The only portion that is not automatic is your server. It must handle the request for the policy file, as outlined. AFTER the policy request is received and a response is sent, that connection is closed. Your original connection request, if allowed via policy, will complete and you can share data client <--> server. A reminder, if you serve the policy on the same port as the data, you need to close your socket as soon as it connects and reconnect again. That's why I suggested specifying a port or using the default 843 and having your server listen to your data port separate from your policy port. So you don't need to reconnect. As quoted here:
Do not expect to reuse the same connection for both a policy file request and a main connection; close the connection after transmitting the policy file. If you do not, Flash Player closes the policy file connection before reconnecting to set up the main connection.
I feel you might think you have more responsibility on a simple data connection than serving the response from the server. You are not doing anything client-side, Flash does that for you.
Here's another resource which makes this all pretty clear:
Adobe Flash Platform * Loading data
The socket policy file doesn't need to be as formal as listed there, do note the linked examples of socket files which are pretty simple:
Copy link to clipboard
Copied
Thank you, and yes it's all working and connecting fine and the policy file works and all, but there seems to just be ONE issue... I found out that when the "writeUTFBytes" in the socketDataHandler is in the socket array, that's when I get the "operation attempted on invalid socket" error. When it's not in the array, I do not get the error.
Basically I found out that, like you said, the socket isn't closed for the policy file, and then when the message is trying to be sent to ALL the sockets in the socket array, it gets the error. I understand I have to close the socket, but I don't know how to do that without it just ending everything and not even letting it be able to move on to the socketDataHandler. Does that make sense? How do I close THAT socket used for the policy file specifically?
Copy link to clipboard
Copied
Flash player in whole simply needs to know the rules. The policy file needs to be read once in which it will remember that policy going forward. When I did it, all I did was have the server use the default 843 port for the policy and did not transmit it over the socket I was connecting to.
That only made me need to listen on 2 ports (for my needs) on my server. Port 843 where I immediately sent any connection requests a policy file terminated by a null byte and then closed the connection immediately. Otherwise, after forwarding the port in my router, I connect to my destination. I did not need to close this socket connection because Flash went off and handled the policy automatically on port 843 (I did nothing to make that happen on the client side). After Flash received the policy, it read the rules and decided if my connection request was allowable. It was, so it connected.
So in async but linear order, this happened from my client:
- I tell Flash to open a socket connection to 1.2.3.4:8888 which is a remote box half way across the world, let's say
- Flash puts that on hold and requests a policy from 1.2.3.4:843
- My server receives that request (because 843 is forwarded to the server)
- My server writes and flushes the policy file back to the client and closes connection
- Flash reads the policy data over the socket and closes connection also
- Flash parses the permissions and decides if I can connect to 1.2.3.4:8888 based on them
- Flash can see I am allowed to connect and continues with the connection attempt at 1.2.3.4:8888
- My server (because 8888 is port forwarded) receives the socket request and allows it
- Having successfully made a connection the server simply writes and flushes "anything you want" to the client to say it is successfully connected
- The client reads that and was expecting it so it may write and flush something to the server to also signal success (handshake)
Might look like a lot but it's pretty simple.
Now here's what you should really be careful with: Scope
Make absolutely certain that you are creating the array that holds the sockets in something that will continue to exist when you need it. Don't make a new function or method in a non-global scope or class that generates the sockets and use a 'var' inside it. e.g.:
function _init() {
_generateSockets();
// now attempt to use it
socks... // FAIL, socks was not received from the return, nor was it global (only existed in _generateSockets() and then was removed)
}
function _generateSockets() {
var socks:Array = new Array();
for (var i:int = 0 ...) {
socks = new ....
}
return socks; // fine but nothing will catch this, effectively dereferencing/removing from memory
}
Just be careful where you create your sockets and do some tracing to actually verify the sockets exist before you attempt to use them. And if you do like I did and leave the policy to port 843, you won't need to close the socket connection at all. The initial creation will be ready for data use, once the policy is handled.

