XMLSocket.send / Socket.writeUTFBytes doesn’t work?
Disclamer
In first words – no, of course it is not broken, it is working fine. But there is a specific issue with these two methods when the socket server is implemented incorrectly. I noticed I’ve been looking for the solution using these phrases and that’s why the post it titled like that.
I just finished writing an ActionScript 3 SWC library which is going to be used with Flex and Flash applications. The library uses Socket connection to provide some statistical information to the Java socket server. As part of the project I had to create simple socket server which simply writes what it gets to the standard output.
The code was working fine when running in Flex Builder debugger. However, as soon as I started the test application in the Flash IDE I found following problem:
- SWF was requesting policy file
- my Java socket server was serving policy file
- SWF was showing that it was connected
- any other messages sent to the socket were not coming through
Exactly the same problem appeared when I deployed Flex version on the external server. It worked while running in the debugger but not from the browser. So it was clearly something wrong with the socket server. Once the policy file was served any other communication wasn’t working. My socket server looked like this:
[sourcecode lang='java']
package uk.co.test;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTest {
public static void main(String[] args) throws Exception {
System.out.println("Starting...");
start();
}
public static void start() throws Exception {
// create socket server
ServerSocket ss = new ServerSocket(1234);
for (;;) {
System.out.println("Waiting for the client.");
Socket cs = ss.accept();
System.out.println("Connection...");
InputStream in = cs.getInputStream();
OutputStream out = cs.getOutputStream();
boolean isConnected = true;
StringBuffer soFar = new StringBuffer();
byte b;
while (isConnected) {
// let it rest a bit:
Thread.sleep(10);
// read everything what's coming in:
while ( (b = (byte)in.read()) > -1 ) {
if ( b == 0 ) {
// zero byte, process:
String value = soFar.toString();
// get the value
if ( value.equals("
") ) {
// policy file requested, sent the policy back:
System.out.println("Policy file request.");
String crossdomain = "";
crossdomain += "";
crossdomain += " ";
crossdomain += " ";
out.write(crossdomain.getBytes());
out.write((byte)0);
out.flush();
System.out.println("Policy file sent.");
} else {
if ( value.equals("exit") ) {
// if exit command received, finish:
isConnected = false;
} else {
// just output the message:
System.out.println(value);
}
}
soFar.setLength(0);
} else {
// append the character to the buffer:
byte[] buf = new byte[1];
buf[0] = b;
soFar.append( new String(buf) );
}
}
}
}
}
}
[/sourcecode]
While looking for the solution I found the following article: Setting up a socket policy file server. Peleus Uhley from Adobe describes how to use policy files effectively. In What data is sent in the request and response? section of the article there is a solution..
Once Flash Player receives the socket policy file, it closes the connection and opens a new connection if the policy file approves the request.
Looking at the above socket server code I could now clearly see what’s wrong. Once the connection is accepted no other connections are coming in until the first client sends exit message. So I modified my socket server, here it is:
SocketTest.java
[sourcecode lang='java']
package uk.co.test;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTest {
public static void main(String[] args) throws Exception {
System.out.println("Starting...");
start();
}
public static void start() throws Exception {
// create socket:
ServerSocket ss = new ServerSocket(1234);
for (;;) {
System.out.println("Waiting for the client.");
Socket cs = ss.accept();
System.out.println("Connection...");
// create socket connection handler and run it in separate thread:
new SocketHandler(cs);
}
}
}
[/sourcecode]
SocketHandler.java
[sourcecode lang='java']
package uk.co.test;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class SocketHandler
implements Runnable {
private Socket _client;
public SocketHandler(Socket s) {
_client = s;
// create new thread from this instance and start it:
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
// get client in/out:
InputStream in = _client.getInputStream();
OutputStream out = _client.getOutputStream();
boolean isConnected = true;
StringBuffer soFar = new StringBuffer();
byte b;
while (isConnected) {
// let it rest a bit:
Thread.sleep(10);
// read everything coming in:
while ( (b = (byte)in.read()) > -1 ) {
if ( b == 0 ) {
// zero byte, process what already came in:
String value = soFar.toString();
if ( value.equals("
") ) {
// policy file requested, send it to the client:
System.out.println("Policy file request.");
String crossdomain = "";
crossdomain += "";
crossdomain += " ";
crossdomain += " ";
out.write(crossdomain.getBytes());
out.write((byte)0);
out.flush();
System.out.println("Policy file sent.");
} else {
if ( value.equals("exit") ) {
// exit command received, exit then...
isConnected = false;
} else {
// just print the message to the stdout:
System.out.println(value);
}
}
soFar.setLength(0);
} else {
// append the char to the buffer:
byte[] buf = new byte[1];
buf[0] = b;
soFar.append( new String(buf) );
}
}
}
} catch (Exception e) {
// ignore
}
}
}
[/sourcecode]
The second socket server fixed the problem. It is working for connections with and without policy requests, from Flash IDE, Flex Builder and the browser.
It took me 5 hours to figure out the solution (process client connections in separate threads) so if you’re in the same situation as I was I hope this post helps you.
January 15th, 2010 at 11:49 am
Hey, very helpful since this policy thing is really a damn.
I will try that, thanks.