So about 2 weeks ago I decided that I should look through requests in the Adobe Cookbooks and see if I can contribute. I found a few that I may work on in the future but the first one I started with was a request for a chat program in AIR using PHP, MySQL, and HTTPService. Really I have a pretty simple solution to this question. I don’t know if this is exactly what the person was looking for but it works.
The short answer is to create a PHP web service that can store messages typed into the chat and return a list of chats after a specific time period. Then setup a timer that polls the webservice every X number of minutes.
Below is the long answer with some sample code.
MySQL Database
Obviously you can store whatever you want in the database. I chose to store very basic information including an id, username, timestamp, message, and IP address.
CREATE TABLE IF NOT EXISTS `chatmessages` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`received` bigint(20) NOT NULL,
`message` longtext NOT NULL,
`ipaddress` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
PHP Webservice
I will include the complete PHP source in the final project as well but as I mentioned the webservice needs to insert a message and get a list of messages after a timestamp. Below is the SQL that I used based on the table above:
$query = sprintf("INSERT INTO `chatmessages` (`username`, `received`, `message`, `ipaddress`) VALUES ('%s', %d, '%s', '%s')",
mysql_real_escape_string($username),
time(),
mysql_real_escape_string($message),
mysql_real_escape_string($_SERVER['REMOTE_ADDR'])
);
$query = sprintf("SELECT * FROM chatmessages WHERE `received` >= %d",
$timestamp
);
AIR Application
You will first need to setup an HTTPService to consume the PHP service. If you are using Flex 4 you can place this in the <fx:Declarations> section of the file:
<fx:Declarations>
<s:HTTPService id="service"
url="{serverUrl}"
useProxy="false"
method="POST"
fault="service_faultHandler(event)"
result="service_resultHandler(event)"/>
</fx:Declarations>
Then setup a timer that runs every 5 seconds or so. When the timer completes you will call the webservice and download all of the messages since the last time you checked. You also probably want to manually download the information anytime the client submits a message.
private function startTimer(length:Number = 5000):void {
pollTimer = new Timer(length, 1);
pollTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerTick);
pollTimer.start();
}
private function timerTick(event:TimerEvent):void {
getChatMessages(lastUpdated);
updateTimestamp();
}
private function getChatMessages(timestamp:Number = 0):void {
service.send({method: OP_GET_CHATS, format: format, timestamp: timestamp});
}
To send a message you simply make a call to the web service:
private function createChatMessage(message:String):void {
service.send({method: OP_CREATE, format: format, username: username, message: message});
}
That’s it!
Conclusion
Other than handling the chat messages that are downloaded that is pretty much it. This really is a simple solution and to be truly functional you would have to add more functionality into the application. For example, you would want to make sure that no one else is logged in with a specific username. You may also want to add in a table that keeps track of who is logged in and logged out. That is slightly more difficult but essentially you would add in a logout button and then also add an event listener to detect when the app is closed. You may also want to add in the functionality to have chat rooms. All of these additions are simply extensions of this application.
The full source code for PHP, MySQL, and AIR are available at http://code.google.com/p/airchatexample/.
Finding a Better Solution
Polling just isn’t as desirable as you might think. A better solution is to use RTMFP which allows you to create P2P connections. For the short term you could create a chat application that uses the Adobe Cirrus service. There are a couple of good sample applications that use Cirrus:
I may, at some future point, do another example using Adobe Cirrus but I haven’t had time to do that yet.