XML Sockets / Flash / PHP Based Chat App

Disclaimer

Ok, I kinda threw this together. This is NOT a tutorial. It's mostly a story of the process I went through. I've included a few code excerpts as well. I plan to eventually write out the entire process and offer some code and all that fun stuff, but I don't have any time to get that together now. I hope it helps. If yo uhave corrections or complaints, let me know.

The Story

A little over a year I ago, I headed a collaboration destined to build a collaboration app. Due to time and financial constraints, we never really got the chance to finish. In order to begin our project, we needed a good way to hold meetings. We all had different IM clients, and none of them really accommodated for groups of 20 or more.

So I went out to find / build a chat application. We needed something that was solid, of course, but also something that would allow us to log our conversations for meeting notes to be searched later.

Most php based apps we found required meta refreshes, hidden frames, obscene amounts of javascript. They were clunky, buggy, required far too much maintainence, devoured bandwidth, and just plain were’t going to get the job done.

I began to search for flash alternatives (this was a couple months pre flash comm server). They were sub par for our needs.

So I began writing my own. I began by checking the source of a couple existing ones that seemed a bit better built (cleaner code, more options, good commenting, etc. After a rudimentary app was built, we held a couple meetings and a few general chats, and here’s the bandwidth for a week of usage, 7 – 20 members at a time (not solid, sometimes there was only 1 or 2 of us for debugging):

Day

Number of visits

Pages

Hits

Bandwidth

14 Oct 2002

40

155002

155405

213.16 MB

15 Oct 2002

36

113319

113540

199.63 MB

16 Oct 2002

35

134919

135417

86.43 MB

17 Oct 2002

33

136966

138894

52.04 MB

18 Oct 2002

43

117788

119876

40.75 MB

19 Oct 2002

24

87249

87915

29.31 MB

20 Oct 2002

26

63699

64660

14.62 MB

Those numbers were absolutely unacceptable, especially considering it was a closed site that only a total of about 25 people even knew existed. So it was all tweaks, making less calls, using shorter messages, and whatever other tweaks I could conjure.

The problem with this method was that it required something very similar to a browser refresh. I would send out a request for new data every x seconds / frames. This is better than a browser refresh, if at least because there was no longer the IE browser ‘clicks’, but was still unreliable and heavy. There was no way to ensure everyone got every message, nor was there a means to ensure everyone was ‘on time’. People with modems had the worst of it.

The Hunt

So back on my search…

I looked into Colin Moock’s unity app:

http://www.moock.org/unity/

I was interested in something I could customize on the client AND server side. Unity is a java based socket server, and from what I’ve heard, a great one, but it costs and it didn’t quite fit with what I needed

I found a flash / perl based app called Chatter.

http://www.pushby.com/chatter/intro.html

This app offered an enormous improvement. If I remember correctly, this is the app that turned me on to xml_sockets (more on that later). Chatter runs a socket on the server via a perl script and listens for the flash client. At the time, it was only live with no local storage, and no ‘rooms’. Nonetheless it was a good start. Besides being based on perl which I hadn’t played with since 97, it was the perfect base. So I dug into the perl sites and added database capabilities and room capabilities without requiring new sockets for each room. I stretched the client out to meet our needs and was overall pleased.

So we used this for about a month with little to no issue. Now we were planning the rest of our app which would essentially be this chat server x 15000. I have NOTHING against perl. Nothing at all. At the same time, I absolutely adore php. If I was about to set off onto a quest to make some enormous socket based app with thousands of features accessed by thousands of people, I needed to go with what I knew. So on to my quest to write a php based socket server.

At first I figured I would just port chatter over, even with all my modifications, Chatter was just too preliminary to our goal application.

Back to research mode:

It was time to learn what a socket actually was, and why it seemed to be the best option yet. How to use it in php, and in Flash (then version 5 and on to MX). Of course all my reference links are dead now (just flipped through them)…

Here’s a couple for further reading:

PHP Sockets: http://www.php.net/manual/en/ref.sockets.php

Dev Shed – XML Sockets: http://www.devshed.com/c/a/Flash/XML-Sockets-in-Flash/

I began writing my own php based socket server based on a tutorial at Dev Shed. I did alright, but not great. I flipped through my referenced and ended up at

http://www.php-tools.de/

These guys have some incredible classes (although some tend to throw a lot of warnings under E_ALL - easy enough to fix, but be warned). Their socket server was perfect for my needs. No extras, clean and commented.

http://www.php-tools.de/site.php?file=patServer/overview.xml

(at the time they didn’t have an many usage examples)

The Server

So I rewrote it my way. I tend to learn better when I rewrite code. In the end it wasn’t too different, but much better understood.

Essentially, it runs through an infinite loop checking the designated port for any communications. If it finds a connection attempt, it adds the client to an array of connected clients, gives it a client id (the index of the array) and runs any processing function you might have designated. Being that it’s a live socket, it also instantly recognizes a disconnect, and runs a designated function accordingly. That fact alone was a huge help in keeping track of who was on and not.

My primary change to the server class was that I needed to be able to set who would receive messages:

function broadcast( $message, $client_id = -1, $exclude_id = -1 ) {}

$message is the message to be sent, which my chat class would format as xml.

$client_id is the client to send the message to If the id was 0 or greater it would send to only one specific client (can be used for instant messages, errors pertaining specifically to a user – like couldn’t log in, or whatever else you can think of). If the id was -1 it would loop through all the clients and send the message to everyone.

$exclude_id was for excluding any particular id(s). This was best for announcing new users (‘soandso’ has entered the room), as there’s no reason for the user who just logged in to see that message.

So now, to send a message to everyone would be:

$server->broadcast(‘my_message’);

To send a message to a particular person:

$server->broadcast(‘my message’, $some_id_zero_or_greater);

So the chat class needed to parse incoming communications and decide what to do with them. I’ve read that it’s better to use regex to parse the xml messages because it’s faster, but I wanted to do this ‘correct.’ I borrowed a sax based xml parser (http://www.devdump.com/phpxml.php), that converts xml to a php array. So now, every time the server gets a message it send it to $chat->process_message($client_id, $message);

The process message function is my chat class’ switchboard. It has only a switch statement, which would activate any other function in my chat class.

function process_message ( $client_id, $message ) {
  $this->xml = new xml($message);
  $this->command = $this->xml->tree;
			
  switch( true ) {	
    case $this->command['login']:
      $username 	= $this->command['login']['attributes']['username'];
      $password 	= $this->command['login']['attributes']['password'];
			
      $this->login( $client_id, $username, $password );
      break;
  }
}

Then the login function would check the username and password against my mysql database, form an xml string as a replay, and broadcast the message back to the client (using the server class by php-tools).

function login( $client_id, $member_username, $member_password ) {
  // Some Query giving us a Boolean $login_ok
  
  if ($login_ok) {
    // Create the login ok message
    $message = '<login login_status="1">'
    . '<user_id>' . $member_array['member_id'] . '</user_id>'
    . '<username>' . $member_array['member_username'] . '</username>'
    . '<login_error />'
    . '</login>' . "\0";
    
    // add the user to the client array
    $this->client_array[$client_id]['member_id']
	  = $member_array['member_id'];
    $this->client_array[$client_id]['member_username']
	  = $member_array['member_username'];
    
    // broadcast our logged in message
    $this->broadcast( $message, $client_id );

    // show the user a list of rooms
    $this->rooms( $client_id );  
  } else {
    
    // create our login failed message
    $message = '<login login_status="0">'
    . '<login_error>Login Problem</login_error>'
    . '</login>' . "\0";

    // broadcast our NOT logged in message
    $this->broadcast( $message, $client_id );
  }
}

And that’s the basics of how to run the server. It becomes a live messaging board of sorts. If you get a new message from the user, you add the message to the database, and then broadcast it to all members.

So how would you implement rooms?

Well, you keep an array that holds all the client_ids within a specific ‘room’. Have the client append the user’s room_id to each message, and then broadcast that message to all clients in that room’s array.

When the client ‘enters’ a room, you add them to the room array, and then broadcast 2 messages. 1 st, to the client who just entered. You send that client a list of all the users in that specific room. Then to everyone else, you send just the added user to them, and their clients add his/her name to their room list.

The possibilities are endless, and I can’t WAIT to get back into this so I can prove that fact.

The Client

Flash comes with an xml parse built in, which does a great deal for parsing the xml formatted messages coming from the server.

First you have to create a connection to your now running php server. To do this you use flash’s XMLSocket functions:

function connect () {
  trace('Connecting...');
	
  // create a new XMLSocket
  socket = new XMLSocket();

  // Define custom handlers for XMLSocket callback methods
  socket.onConnect  = enoOnConnect;
  socket.onXML      = enoOnXML;
  socket.onClose    = enoOnClose;

  // connect
  if (!socket.connect(host, port)) {
    trace ("Sorry, I could not connect to the chat server.");
  }
}

enoOnConnect, enoOnXML, and enoOnClose are event handlers. Basically that means that when the connection ‘broadcasts’ a specific event, those functions will be run.

enoOnConnect(success) gets a message from the connection object that says connected ok, or connection failed. From there you can either popup a login form, or show an error message.

enoOnXML(message) receives any xml the server sends to this client. You send this message to your parsing function (which will then use flash’s xml parsing capabilities and act as a client side switchboard similar to our server side one above.

enoOnClose() fires off when the connection object somehow gets disconnected form the socket. If your internet connection should go down, or if the server shuts down or even just closes the connection to the client. This is a good place to run any cleanup functions you might want as well as possibly a ‘reconnect’ form.

So now you have what you need to open a live socket connection from a flash client to a php based socket server. What you do with the connection is your call. Of course You’ll need a means of parsing your messages and such.

I recommend writing yourself a slim document explaining what xml messages will do what at this point. You will need to implement these messages on both the server and the client.

As far as the mechanics of parsing a message, I HIGHLY recommend using a message queue. You may potentially be receiving hundreds of messages at any moment if you have a live chat, and the client may not be able to catch up.

So you have enoOnXML add the message to the queue:

function enoOnXML (message) {
  message_queue.push(message);
}

Then you have a movie clip consitently checking the size of the message_queue array length. If it has any, have it start ‘shifting’ messages off and processing them. You want to shift and not pop so that you will be processing them in order.

Here’s an excerpt from my current processor MC:

if (_root.message_queue.length > 0) {
  var message = message_queue.shift();
  switch(message.childNodes[0].nodeName) {
    case "login":
      var login_status
	    = unescape(message.childNodes[0].attributes.login_status);
      var user_id
	    = unescape(message.childNodes[0].childNodes[0].firstChild.nodeValue);
      var user
	    = unescape(message.childNodes[0].childNodes[1].firstChild.nodeValue);
	
      if (login_status) {
        _root.member_id = user_id;
        _root.login_form.removeMovieClip();
      } else {
        trace(login_error);
      }
      break;
  }
}

So this process is checking the command sent by the server. If the command is called ‘login’, it grabs the variables from the xml and acts accordingly. If login_status is true, it sets the client’s user_id (For use when we send messages later) and closes the login form. Otherwise it shows an error. Of course you could have it show a popup at that poin for a forgotten password or a middle finger or whatever you prefer.

That’s the idea behind a chat application based on Flash, xml_sockets, php, php sockets.

Here’s an example of a conversation between my client and my server:

client:
<login u="enobrev" p="mypassword" />

server checks the db and login is ok, sends a login ok message:
<login ok="1" id="6" />

server sends a list of rooms the user can enter:
<rooms>
  <room id="1">Lobby 1</room>
  <room id="2">General Chat</room>
</rooms>

client gets the rooms list and allows the user to select one. Once selected, the client sends a message containing the selected room id:
<room id="1" uid="6">

server adds the user to the room and loops through all 'users' in 'room' array and broadcasts the following:
<msg from_id="0">*username has entered</msg>

client now sends a message to the room:
<msg from_id="6" room_id="1">Hello everyone</msg>

server script would loop through the room array and broadcast the message to all users in that room. This way you could essentially even allow people to send messages to multiple rooms (like admin messages, etc):
<msg from_id="6">Hello everyone</msg>
Er.. that's it for now

I hope all of this helps. I realize it’s a bit all over the place and is not much of a tutorial, but more of a story about getting a chat together. As my time is limited, rent is soon due, and my chat application is long unfinished, this is the best I have to offer.

Feel free to contact me should you have any questions. I’ll be glad to help to the best of my abilities.

With the new versions of flash and php out this can become a far more robust and modular system, and if you’re working on one, let me know. I may be interested in getting in on it.

The idea that you could now write the flash processing in separate classes makes my mouth water. I’ve been working on an as2 based app for the past 3 weeks, and I must say, flash has finally grown up.

Oh, as a side note, the only major roadblock I ran into was getting my host to allow me to run the server script at all times. When I started the script from ssh or browser it would time out. By the time my host finally gave me the ability to run the script as a process, I'd been far off into other projects.

Good luck!

March 17, 2004