Building a Multi-user Chat Server with xSocket, Java, and Flash CS3

If you followed my recent tutorial on how to build a socket server for Flash CS3 using xSocket and Java, then you will be well prepared to go through this exercise to build an actual chat server in which Flash (or Flex) clients can connect and chat to each other.

This will be a simple socket server and things like validation for the most part I’m going to ignore to keep the code short and easy to follow. I’ll also be dropping the ActionScript into the timeline for the sake of speed and simplicity. Like the first socket server I built this one will be able to be to shut down remotely and obviously something like this on a production server would need to be protected rather than wide open as I’ll code it for now.

I used the following to build this:
1. Netbeans 6.1
2. Java JDK 1.6
3. xSocket 2.2 (core)
4. Flash CS3 (CS4 should work fine too, you can also use Flex if you wish – just be aware you will need to modify the Flash part to work with Flex instead)

At this point if you haven’t walked through the original tutorial on this topic, then please do that now. From here on I’ll assume the original project was working for you and you understood the concepts. To avoid confusion, I’m going to put this into a new project even though some of the code is exactly the same of whats in the original project code.

Open up Netbeans and create a new Java application project and call it “xSocketChatServer”. Go into the “xsocketchatserver” package and a class file called “xSocketDataHandler.java”. A “Main.java” file should already be in the project. Don’t forget to right-click on the “Libraries” and add the reference to the “xSocket-2.2.jar” file.

The code in Main.java is going to be exactly what it was in the original post:

package xsocketchatserver;

import org.xsocket.connection.*;

public class Main
{
    protected static IServer srv = null;

    public static void main(String[] args)
    {
        try
        {
            srv = new Server(8090, new xSocketDataHandler());
            srv.run();
        }
        catch(Exception ex)
        {
            System.out.println(ex.getMessage());
        }
    }

    protected static void shutdownServer()
    {
        try
        {
            srv.close();
        }
        catch(Exception ex)
        {
            System.out.println(ex.getMessage());
        }
    }
}

That requires no explanation since it is the same as the original code. Lets move onto the “xSocketDataHandler.java” file now.

The first thing you will notice is I’ve implemented the IDataHandler just like before, but I’ve now also implmented the IConnectHandler and IDisconnectHandler. Implementing those two new interfaces will allow us to to know when people connect or disconnect from the server.

public class xSocketDataHandler implements IDataHandler, IConnectHandler, IDisconnectHandler

Just below that code we have this:

private Set<INonBlockingConnection> sessions = Collections.synchronizedSet(new HashSet<INonBlockingConnection>());

Since we are going to have multiple people connected we are going to need a way to talk to all of them when someone sends a message. So when Client A sends a message or connects, we want to loop through all the open connections to send the notifications to Client B, Client C, etc.

Moving on we notice the onData method has changed quite a bit from the original.


public boolean onData(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException
{
    try
    {
        String data = nbc.readStringByDelimiter("\0");
        // Expected Message Format:
        // USERNAME~MESSAGE
        // ie: JohnDoe~Hello World

        if(data.trim().length() > 0)
        {
            System.out.println("Incoming data: " + data);

            if(data.equalsIgnoreCase("<policy-file-request/>"))
            {
                nbc.write("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"8090\"/></cross-domain-policy>\0");
                return true;
            }

            String[] message = data.split("~");

            sendMessageToAll(message[0], message[1]);

            if(message[1].equalsIgnoreCase("SHUTDOWN"))
                Main.shutdownServer();
        }
    }
    catch(Exception ex)
    {
        System.out.println("onData: " + ex.getMessage());
    }

    return true;
}

You can see we expect a specific message format: USERNAME|MESSAGE

Ok, before we go any further, we need to remind ourselves the Flash part of this uses the XMLSocket class. We aren’t passing XML are we? The XMLSocket class does not force you to use XML, but you might want to depending on the complexity of your messages. For this simple project though it would be overkill.

Next we check to see if the Flash client is requesting a policy file, and if so, we need to pass one to it. In this case I pass a very open policy file. In a production system this would be set according to your preferences of how open you want it. Flash (and Flex) will always ask for the policy file in this format:

<policy-file-request/>

Once the policy file is taken care of, when the client sends a message we simply pull apart the incoming data and send it off to the method “sendMessageToAll” to be sent out to everyone connected. Also note that if I was to send the text “SHUTDOWN” from the Flash client the chat socket server would shutdown – as it stands anyone connected could do this. This is nice to have when testing and since I haven’t implemented the java.io.Closeable interface in this project we have no other choice right now other than to do a brute force kill. Keep in mind that xSocket does indeed work just fine with java.io.Closeable.

Lets look at “onConnect” and “onDisconnect”:

public boolean onConnect(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, MaxReadSizeExceededException
{
    try
    {
        synchronized(sessions)
        {
            sessions.add(nbc);
        }

        System.out.println("onConnect");
    }
    catch(Exception ex)
    {
        System.out.println("onConnect: " + ex.getMessage());
    }

    return true;
}

Notice this is where our “sessions” variable comes to life. As clients connect we store their connections into the sessions variable, this way we will know how many people are connected, we can send messages to all of them, some of them, or only one of them as we so choose. For this project we always send the messages to everyone connected.

onDisconnect should then simply remove the “dead” connections:

public boolean onDisconnect(INonBlockingConnection nbc) throws IOException
{
    try
    {
        synchronized(sessions)
        {
            sessions.remove(nbc);
        }

        System.out.println("onDisconnect");
    }
    catch(Exception ex)
    {
        System.out.println("onDisconnect: " + ex.getMessage());
    }        

    return true;
}

Finally this takes us to the “sendMessageToAll” method:

private void sendMessageToAll(String user, String message)
{
    try
    {
        synchronized(sessions)
        {
            Iterator<INonBlockingConnection> iter = sessions.iterator();

            while(iter.hasNext())
            {
                INonBlockingConnection nbConn = (INonBlockingConnection) iter.next();

                if(nbConn.isOpen())
                    nbConn.write("<b>" + user + "</b>: " + message + "<br />\0");
            }
        }

        System.out.println("Outgoing data: " + user + ": " + message);
    }
    catch(Exception ex)
    {
        System.out.println("sendMessageToAll: " + ex.getMessage());
    }
}

Here we loop through our sessions variable checking to see if the current iterated session is still “open” and if so, we send the message that was passed in. In this case I do a little HTML formatting since we will be displaying the results in Flash’s TextArea control with the limited HTML rendering it can do.

So the final code then for xSocketDataHandler looks like this:

package xsocketchatserver;

import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.*;
import org.xsocket.*;
import org.xsocket.connection.*;

public class xSocketDataHandler implements IDataHandler, IConnectHandler, IDisconnectHandler
{
    private Set<INonBlockingConnection> sessions = Collections.synchronizedSet(new HashSet<INonBlockingConnection>());

    public boolean onData(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException
    {
        try
        {
            String data = nbc.readStringByDelimiter("\0");
            // Expected Message Format:
            // USERNAME~MESSAGE
            // ie: JohnDoe~Hello World

            if(data.trim().length() > 0)
            {
                System.out.println("Incoming data: " + data);

                if(data.equalsIgnoreCase("<policy-file-request/>"))
                {
                    nbc.write("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"8090\"/></cross-domain-policy>\0");
                    return true;
                }

                String[] message = data.split("~");

                sendMessageToAll(message[0], message[1]);

                if(message[1].equalsIgnoreCase("SHUTDOWN"))
                    Main.shutdownServer();
            }
        }
        catch(Exception ex)
        {
            System.out.println("onData: " + ex.getMessage());
        }

        return true;
    }

    private void sendMessageToAll(String user, String message)
    {
        try
        {
            synchronized(sessions)
            {
                Iterator<INonBlockingConnection> iter = sessions.iterator();

                while(iter.hasNext())
                {
                    INonBlockingConnection nbConn = (INonBlockingConnection) iter.next();

                    if(nbConn.isOpen())
                        nbConn.write("<b>" + user + "</b>: " + message + "<br />\0");
                }
            }

            System.out.println("Outgoing data: " + user + ": " + message);
        }
        catch(Exception ex)
        {
            System.out.println("sendMessageToAll: " + ex.getMessage());
        }
    }

    public boolean onConnect(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, MaxReadSizeExceededException
    {
        try
        {
            synchronized(sessions)
            {
                sessions.add(nbc);
            }

            System.out.println("onConnect");
        }
        catch(Exception ex)
        {
            System.out.println("onConnect: " + ex.getMessage());
        }

        return true;
    }

    public boolean onDisconnect(INonBlockingConnection nbc) throws IOException
    {
        try
        {
            synchronized(sessions)
            {
                sessions.remove(nbc);
            }

            System.out.println("onDisconnect");
        }
        catch(Exception ex)
        {
            System.out.println("onDisconnect: " + ex.getMessage());
        }        

        return true;
    }
}

Wasting no time we can move onto the Flash client. Open up the Flash IDE and create a new ActionScript 3 project and call it “xSocketChat.fla”. I’m not going to go over constructing the user interface, you can get that from the project files. I want to instead focus on the ActionScript part of this. Still, lets take a look first at what the chat UI actually looks like:

Very simple, and very ugly but more than enough to prove the chat server will work.

Here is the ActionScript:

errorIndicator_mc.visible = false;

var xmlSocket:XMLSocket = new XMLSocket();

xmlSocket.addEventListener(DataEvent.DATA, onIncomingData);
xmlSocket.addEventListener(Event.CONNECT , onConnected);
connect_btn.addEventListener(MouseEvent.CLICK, onConnectClicked);
send_btn.addEventListener(MouseEvent.CLICK, onSendClicked);

function onConnectClicked(evt:Event)
{
	errorIndicator_mc.visible = false;

	if(username_txt.text.length <= 0)
	{
		errorIndicator_mc.visible = true;
		return;
	}

	xmlSocket.connect("127.0.0.1", 8090);
}

function onConnected(evt:Event)
{
	connect_btn.enabled = false;
	xmlSocket.send(username_txt.text + "~has connected");
}

function onSendClicked(evt:Event)
{
	if(sendChat_txt.text.length > 0)
		xmlSocket.send(username_txt.text + "~" + sendChat_txt.text);

	sendChat_txt.text = "";
}

function onIncomingData(event:DataEvent):void
{
	incomingChat_txt.htmlText += event.data;
}

We use the same XMLSocket class we used in the first tutorial and then we add some event listeners. Two of which relate to the socket, one will listen for incoming data and the other will listen to see if we connect. There are several more listeners we could have used (like listening for an error), but we’ll stick with these to keep our motto of “simple” going. If the user doesn’t supply a username we prompt them to do so with a little pulsing red circle by the username textbox. Other than that I don’t do much other validation. When the user types in a user name and presses the connect button we connect them to the socket server with the address and port it’s listening on which in this case is the localhost address and port 8090.

When they type in some chat text below and hit the send button we send it off to the chat socket server which will turn around and send it back to everyone that is connected including us. This will trigger the “onIncomingData” where we grab the data and use the textarea’s “htmlText” property to display the data. We use htmlText since we know the server formatted it that way for us.

Start running the Netbeans project first and then run two or more of these Flash chat clients and see how it works.

Something to add now might be a “Disconnect” button. The XMLSocket class has a “close” method you can call.

So there it is, a simple multi-user chat server for your Flash/Flex clients. With a little work this can be turned into a full fledged chat or game server.

Here are the project files.

26 Responses to “Building a Multi-user Chat Server with xSocket, Java, and Flash CS3”

  1. [...] 1. Netbeans 6.1 2. Java JDK 1.6 3. xSocket 2.2 (core) 4. Flash CS3 (CS4 should work fine too, you can also use Flex if you wish – just be aware you will need to modify the Flash part to work with Flex instead) ????? http://giantflyingsaucer.com/blog/?p=224 [...]

  2. Glenn says:

    Nice, I’ve been looking for a tutorial like this! Thanks! :D

  3. God bless you real good for this tutorial. It really came in handy.

  4. Sander says:

    Thank you for your nice tutorial. I followed everything and it works really great! I have one newbie question though…. it is running on my computer now. How do I put this online? Could you give me some directions?

    Kind regards,
    Sander

  5. Chad Lung says:

    @Sander,

    You will need to host with someone like http://www.linode.com or someone with similar services. They will allow you to run the server and usually give you a static ip as well.

    Chad

  6. Sander says:

    @ chad Thank you for your prompt response! I will look into your suggestions….

    Sander

  7. Joe says:

    Thanks for the great tutorial!

    I’m trying to upload this to my website so I can test it thurver with other people and start using this to make something better like a small game for the sake of learning.

    When connecting to the server through the swf that I have uploaded to the internet it requests the policy file, and the server returns it, like it should, but I still recieve a security error (Error #2048: Security sandbox violation: [My swf] cannot load data from [My server]) – This works when ran locally, as it dosnt request the policy file.

    I’ve tried changing the policy files to loads of different things from examples i’ve found from googling but now i’m beginning to think that it isn’t the policy that is causing the problem.

    Any ideas about how I could fix this problem?

    Thanks in advance,
    Joe

  8. Chad Lung says:

    @Joe,

    Take a look here:
    http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security_04.html
    - in particular you will want to turn on the Flash Player’s logging. That will give you a much better idea of where the problem is.

    Chad

  9. blue says:

    Thanks, this was great tutorial! Now can you say how to do different rooms and/or how to send data to particular user among several?

  10. Chad Lung says:

    @blue,

    Take a look at GFS Server (my open source project on Google code which is a socket server for Flash/Flex) because it does all that and more.

    http://code.google.com/p/gfs-server/

    Chad

  11. Cody Cero says:

    Ok I have put this tutorial far beyond what it displays here and without using anymore server side coding than whats displayed here. Yeah, I have made a username & password signup & login. A character creation and selection screen. I have made the in chat area as well but it is really well made.

    I gave the chat area, viewable by all users and moveable by the user it belongs to. Along with a scrolling floor underneath the player and can move up and down on the sloped surface. I used user.x = providedX + YourGroundX – ProvidedGroundX in order to calculate the users x according the position difference of your ground and his own ground. So that was solved.

    So all is cleared up client wise so far (still want to add a bit more though, like I’m almost done with the friends list for example) but the problem is that after I logged in with 6 clients (all on my comp done by me) it started lagging. I could be wrong and it was because I was running too much at once. But I could be right and the server can’t stand that many looped sendings of the whole object I’m sending over the server (compressed and transformed into string).

    Can anyone come up with an answer on how to speed up the server?

  12. Chad Lung says:

    @Cody,

    Sounds like something you might want to post on the xSocket mailing list, or try my open source socket server:

    http://code.google.com/p/gfs-server/

    Chad

  13. Lorenz says:

    Hi, Great tutorial!
    I am looking at the GFS source code on google.
    In specific i need the part on how to add or remove rooms to this chat.
    Can u explain me on what file of the src directory to look?
    i am kinda lost :P

  14. seangheng says:

    after i got your file, i upload it on my server site, http://www.seangheng.000space.com/index.html, but it doesn’t work and always show text as below:
    Error #2044: Unhandled SecurityErrorEvent:. text=Error #2048: Security sandbox violation: http://www.seangheng.000space.com/xSocketChat.swf cannot load data from 127.0.0.1:8090.
    at xSocketChat_fla::MainTimeline/xSocketChat_fla::frame1()

  15. Hi,

    I have been trying the code and works fine when I run the code within both IDEs, Eclipse for java and Flash CS4 for the client. Well, if I run the client outside the Flash environment, it doesnt work and the Server doesnt receive any incoming data. Does anyone know the problem? Whats wrong?

  16. Rogier says:

    Thanks for the tutorial it works like a charm!

    But I have a question, can you read out the session list outside a thread?

    I am trying to read it with a timer, but it remains empty if I try to read it with the same technique as in the threads…

  17. eric says:

    I was hoping to use this with a game and I got the demo working fine;really well done. I was thinking that I should let all the computers update the server and then send out the data I one batch rather than at one time for efficiency. All the examples I see send back the data immediately. Is this the best way? I tried to create an array with the updated values in my data handler class but it does not seem to hold the values. I also put a timer there and was going to update all clients with the arrays contents. I am not sure if this is related to ho the thread process data or I am doing something wrong. The values come in but when the timer hits the values are different. Any help on how best to do this would be greatly appreciated.
    Thanks,

  18. Seantron says:

    Yeah, sorry, but this totally doesn’t work anymore.

    Here’s what is going on:

    onConnect
    Incoming data:
    Cross Domain is written
    Data Failure: channel is closed

    So yeah, that’s of course on a live server. If you run it locally of course it works like a charm because the stupid Policy isn’t even called.

    If anyone has gotten this to work on a Live Server, I’d be very interested in what you had to do.

    Thanks.

  19. Seantron says:

    Fixed it guys.

    If anyone is interested. I made a secondary server which listens on port 843, and only writes up Policy Files. Then I connect to the one that is on 8090 with no problems at all.

    Bam.

  20. Chad Lung says:

    @Seantron,

    Have you turned on the Flash Player Debugging/Logging? I’d try that with your live server before having to send the policy file through port 843 then connecting to 8090.

    Chad

  21. Prakash says:

    Hi,

    plz any one help me to solve this. i created a sample application similar to this tutorial(i copied almost this tutorial code). im able to revieve messages from flash’s XMLSocket to java server but im not able to send any data from java to XMLSocket. im getting the below error message in java console.

    channel is closed (read buffer size=0)

    thanks and regards,
    Prakash

  22. Sebb says:

    Hey!

    started learning java recently, and have been trying to do a php socket server to no avail, but this is da |33t ro><0rz!

    thx alot

  23. This tutorial is great! I’ve been wanting to do this for a while now! I have a question though. Right now I have a Linux hosting account through GoDaddy. I want to get this up on the internet somehow. Is it possible with my current setup? And if so, how? All help would be great!

  24. Chad Lung says:

    @Aaron,

    You need something like this on GoDaddy:
    http://www.godaddy.com/hosting/virtual-dedicated-server.aspx

    I had mine running on linode.com without any problems (and cheaper than GoDaddy).

    Chad

  25. Aaron says:

    Awesome! I’ve returned to working on this finally! I have been able to get everything working on a remote server with multiple clients (using Dreamhost). I do have a question I was hoping you could help with. What do you recommend for “labeling” each of the connections? An example of what I want to do is if the chatroom has 5 users: UserA…UserE and let’s say UserB sends a message specifically to UserC. Is there a way that Java can check the “label” of the connection as it iterates through the Set of connections? Thanks in advanced for the help!

  26. Chad Lung says:

    @Aaron,

    I’m not sure I understand your question. You can go through the connections and get details on each.

    Chad

Leave a Reply