Java meets Flash
Persistent TCP/IP Socket Connections with Variable-Length Messages. ie,
Tuning Java for Serving a Flash 5 XMLSocket Client.

Macromedia introduced an interesting convention for persistent sockets with its Flash 5 XMLSocket object. Once the client connects to a server, asynchronous bidirectional communication begins (meaning, both client and server may "talk" freely, even at the same time).

There are some simple guidelines. Small XML documents of variable length are passed back and forth between client and server. XML is the assumed message format, so the ?xml? header is skipped. The EOM (end of message) marker is a single byte with a byte value of zero. I think they chose this because that's how you mark the end of a String in C and C++.

Flash clients have long been able to speak http, but http can get inefficient and slow. This is because it follows a request-response model. In request-response, an http client always speaks first (making a request) and then hands over the microphone to the server. In http, once the server is done responding, it closes the connection. Any additional exchange must happen in a new connection.

There can be a lot of overhead establishing each connection, so keeping the line open is a very attractive new option. This is especially true in applications where latency is an issue, like games and chat.

On the Flash side, much of the dirty work in the XMLSocket protocol is handled behind the scenes. This includes buffering the data received from the server and firing off a message handler when each EOM delimiter is encountered.

On the server side, you may have to get your hands dirty teaching Java about this new protocol. I'll assume you're using Flash for the client and Java on the server. Java on the server makes a lot of sense because it has mature, easy-to-use threading and socket APIs. From the simplest chat server to the hairiest multi-user game server, Java can get the job done quite elegantly. Try out my multiplayer blackjack on www.dagnammit.com for a simple example. Flash on the client, Java on the server.

Here are some of the pitfalls I encountered while implementing the protocol on the server with the java.net.Socket class:


(1) This protocol involves frequent transfer of very short messages. The default Socket object has a built-in delayed-send feature that is tuned to work great for large file transfers. Turns out, it really sucks for this short message protocol! Make sure as soon as your socket is created that you turn this off:

Socket sock = serverSock.accept();
sock.setTcpNoDelay(true);

Otherwise you will see long delays between when you flush your socket's output stream and when the socket actually decides to send the bytes.


(2) There is a curious bug in the following code (part of a thread which stores socket input and reacts to incoming messages):

...
InputStream in = sock.getInputStream();
StringBuffer soFar = new StringBuffer();
byte[] buf = new byte[1024];
boolean isConnected = true;
// loop forever...
while(isConnected) {
    // play nice with the other threads and surrender the CPU
    Thread.sleep(20);
    // collect all the bytes waiting on the input stream
    int avail = in.available();
    while (avail > 0) {
	int amt = avail;
	if (amt > buf.length) amt = buf.length;
        // THE WRONG WAY see below for the right way
	in.read(buf, 0, amt);

        int marker = 0;
        for (int i=0; i<amt; i++) {
            // scan for the zero-byte EOM delimiter
            if (buf[i] == (byte)0) {
                String tmp = new String(buf, marker, i - marker);
                soFar.append(tmp);
                handleMessage( soFar.toString() );
                soFar.setLength(0);
                marker = i + 1;
            }
        }
        if (marker < amt) {
            // save all so far, still waiting for the final EOM
            soFar.append( new String(buf, marker, amt-marker) );
        }
	avail = in.available();
    }
}
// mop up...
...

When the input stream reports how many bytes are available to read "without blocking," I expected the call to in.read() to grab exactly that many bytes. Most of the time it does grab the number of advertised bytes. However, I noticed every once in a while a message would get mangled.

I finally pinpointed the problem and realized that in.read() often kicks out after reading less (sometimes considerably less) than what .available() reported. Fortunately, in.read() returns the number of bytes actually grabbed. The above code is easily amended to work around my incorrect assumption. Just change this line:

in.read(buf, 0, amt);

to this:

amt = in.read(buf, 0, amt);

One last caveat -- if you plan on having 20+ simultaneous connections you will need to consider some form of thread pooling. Rather than creating a new thread for each new socket, I try to assign multiple sockets to a single event-delegation thread, in some ratio that won't noticeably affect performance. This thread periodically wakes up and cycles through all of its sockets, checking for any incoming messages on each one in turn before going back to sleep.

Each thread consumes considerable system resources, so reducing the total number of threads in this way can make your server app a lot more scalable.


(3) You will no doubt manage multiple sockets in your server app. Much of the server code will deal with broadcasting information to all sockets or from one client to another. Here is some sample Java code for sending a string containing an XML-formatted message. Key elements are sending the EOM (end of message) marker after each message and flushing the output stream in case it is buffered.

private void sendXml(String xml, Socket sock)
throws java.io.IOException
{
    if (xml == null) return;

    // make sure other threads aren't already writing to this socket
    synchronized (sock) {
        OutputStream out = sock.getOutputStream();
        // if your app passes very large messages around you may
	// want to add buffering here since otherwise this will
        // allocate many large byte arrays.
	out.write( xml.getBytes() );
        out.write( (byte)0 );
        out.flush();
    }
}

You will want to make sure that this is the only bit of code that manipulates the OutputStream of your sockets. By forcing all outgoing communication for a particular socket through this thread-synchronized point, you can avoid one thread stepping on another's toes and mangling both messages.

For example: When thread A hits the synchronized block, it finds no "lock" on the sock object, places its own lock and proceeds. While it is writing, thread B comes along and wants to write something to the same socket (for example, a ping thread that sends a <PING /> message every ten seconds to ensure the connection is still good). Thread B reaches the synchronized block and discovers thread A's lock. Thread B queues up and must block until thread A exits the synchronized section and releases the lock.

Mangle prevented.


That's all for now! Happy coding...
  -tom

Tom J. McClure
Top Dog
Black Dog Hosting

All Articles:
  • Java meets Flash (server/client)
  • Freedom of Position (dhtml)
  • Freedom of Position II: Date Picker (dhtml)
  • Video Poker Obsession (optimization)
  • Tag, You're It! Chunk, the All-Purpose Template Engine (java)
  • Look, Ma! Real Scroll Bars! (flash 5)
  • Perl, curl, and HTTPS Post for XML