While waiting for my current contract to get renewed + over the Thanksgiving holidays, I’ve been playing around with AIR 2. Â To me, the coolest new feature is a new class called “ServerSocket“. Â It’s like the existing Socket class, except it can “listen” for incoming Socket connections. Â That’s right, you can make an AIR application that is a server application AND optionally run the sucka via command line instead of making a GUI for it. Â How frikin’ cool is that!?
I’ve always been a fan of multiuser applications using Flash Player. Â The main reason I got into programming in the first place was I hoped I could create communication applications that brought people together, and thus I could die saying I’d left the planet in a better place than I’d found it. Â I’ve never been very skilled at server side development, so even simple “polling chat apps” were beyond my technical ability to create within a reasonable time frame.
My first time having fun with these types of apps was XMLSocket. Â We were creating a medical scheduling application, basically a real-time, multiuser Outlook calendar for Doctors & Nurses back in 2002 via Flash Player 6. Â The 2nd, and most fun I’ve had was when Flash Communication Server (now known as Flash Media Server) was released back in 2003. Â Using a Webcam, a Microphone, and a tincy bit of ActionScript 1 on the server, you could create real-time, multiuser chat applications with video & audio. Â As time went on, more and more server technologies came out to help with these types of apps such as more XMLSocket servers, and competitors to FMS. Â Even the recent Live Cycle Collaboration Services, while definitely more than reasonable for larger apps at first glance, is just way out of budget for my smaller apps based on even simple math. Â I have heard of 1 company my man Kristofer works for who apparently has had great success using it, lucky bastard.
The problem? Â If you don’t have the server know how and/or resources, then they are all insanely expensive.
There were tons of apps I, and others, wanted to create, but just couldn’t find a way to make them profitable given the overhead costs, mainly associated with setup and monthly fee’s. Â Notice I didn’t say “bandwidth”, since even then you could get chat bandwidth cheap or even free. Â I’ve still tinkered throughout the years, but never really found a way to make any of my ideas last longer than a few months based on thousands of dollars of up-front investment in paying for a year’s license of FMS or hiring the technical expertise for someone to setup & maintain a Red5 instance for me for example. Â Even more frustrating is none of my clients ask me to do real-time apps. Â I’ve gotten 1 in my entire career. Â I HAVE heard of a ton of my fellow consultants using Blaze DS, but I apparently don’t get those jobs.
Now with ServerSocket, we can create our own local servers. Â For small scale applications such as games and chat that only a specific group of people will be interacting with, this is PERFECT! Â Obviously you still need some way for Person B to know what Person A’s IP address is, but there are already plenty of existing technologies to do this, including the existing middle-tier ones many use currently. Â Even better, only the server has to be written in AIR; existing browser based Flash Player clients going back to Flash Player 9 can connect to your server since they have the Socket class. Â The opportunities here really are endless.
AIR 2 offers the ServerSocket class, allowing you to listen for incoming binary Socket connections. Â The Socket connections can come from another AIR application, or a SWF running the browser. Â This allows you to create real-time, multiuser applications using Flash Player. Â The AIR app can just be a server, or an application in it’s own right.
To illustrate how cool this functionality is, I created a chat server and client. Â The client is written in AIR as well, but it certainly doesn’t have to be, just the server. Â It was just easier for me to ignore the Flash Player security sandbox, and test locally, so I used AIR. Â What’s great is the chat server just runs from your computer, no server needed. Â All you need is an internet connection and AIR 2 installed, and you’re good to go!
There are 3 things I’d like to talk about. Â How the ServerSocket works, an overview of the code, and what I learned.
The ServerSocket listens for incoming TCP/IP connections on a specific IP address and port. Â When they attempt to connect, you can accept or reject this connection. Â Once connected, any data that the Socket sends, you can listen for directly on the Socket; it’s a normal flash.net.Socket. You can also send those Sockets data as well. Â These connects, while being a flash.net.Socket class, may not actually be from a Flash Player. Â For the sake of this blog entry, I’m assuming they will be.
For example, Weyert de Boer sent me a web server he created to help me get started learning ServerSocket. Â You can hit it like a normal web server, and it’ll send back standard HTML pages. Â Instead of hitting Apache… you’re hitting an AIR app. Â Dope, right!?
Code Base Overview
There are 4 Flex projects that make up the chat server & client. Â The ChatServer, ChatClient, ChatLib, and ChatTextBlueSkin. Â The ChatServer holds all the code to run & show the Chat Server. Â The Chat Client holds all the code used to run & show the chat client. Â The ChatLib holds shared code amongst the ChatServer and ChatClient; basically core networking code used for chatting & sending messages over the Sockets. Â The ChatTextBlueSkin holds the graphical components so you can see them in Design View.
If you want to just see how ServerSocket works, ChatServer has that code, and part of the core messaing is in ChatLib.
Also, I go over a lot of specific classes, so knowing Control/Apple + Shift + R in Flex Builder will help you quickly find these classes.
The ChatLib holds all the core messaging stuff. Â It took me 3 tries to get this “right”. Â It’s still not right, but close enough.
The InstructionConstants holds all the messages the client and server send to each other. Â If a message is sent that isn’t in this constants file, the client and server ignore it. Â The 4 types of messages that can be sent are created as Message ValueObjects so I can have a strongly-typed way to deal with them. Â I don’t actually send the Message VO’s over the wire. Â Technically I could writing them to a ByteArray, but I used JSON so I could more easily see what was happening since small JSON messages can be read easily vs. the binary ByteArray. Additionally, as I found out later, it’s easy to parse out multiple JSON messages off of a Socket.
You’ll notice they have the ability to convert themselves to JSON, and back again. Â This delegates the responsibility of parsing messages to the messages’ themselves, since they most easily know how to serialize & de-serialize themselves. Â I’m usually a major proponent of using a central Factory class to do this so you can more easily share code, centralize parsing logic, and catch all parsing errors in one place… but it’s just not as flexible when you’re trying to quickly play around with formatting and your Message data changes often, so it is what it is. Â When a message comes “off the wire”, I take the raw JSON, and get one of those messages. Â When I want to send a Message, I convert it to its JSON equivalent before sending.
The actual creation of Messages from off a Socket (both Server and Client), happens via the MessagesFactory::getMessagesFromSocket function. Â Re-writing code in this Factory class 3 times is where I learned how Sockets work. Â What I thought happened was when you send a message via Socket.flush(), that it was sent to the server, and you got an event for it. Â However, there is NO correlation between the number of flushes you make, and the amount of socketData events you receive on the receiving end. Â Worse, there is no progress. Â What this results in is that sometimes you’ll 1 message on the Socket, and other times 3… it’s random. Â Thus, your parser has to be smart enough to be able to parse off multiple messages from the ByteArray that is Socket.
Using JSON, that’s easy; you just split the String at the ending bracket “}”. Â GETTING to that being easy took 4 damn days. Â If you create your own message format, make it easy for yourself, and create your own message ending character. Â XMLSocket uses a 0 byte. Â In effect, I could get 1 or multiple JSON messages, and thus the Factory class is capable of parsing all of them.
Another thing too I didn’t know; when you read data off the Socket, it’s immediately gone. Â I started getting weird bugs where I’d check for a message’s type, read it, and it’d be a different message, and I was like, “WTF?”. Â Socket doesn’t have a position variable like ByteArray does allowing you “rewind” to the beginning. Â Once you read it, it’s gone, so you better store the mofo. Â Imagine a hose you can’t turn off in the desert.
I wrapped the Socket class via Composition using MessageSocket. I needed a place to store all the messages that can arrive on the Socket. Â MessageSocket does just that; it dispatches events when something breaks just like Socket does and informs you new messages came in, 1 event for 1 message; easy to code to. Â Easier than Socket, anyway.
Early on, I wanted to confirm if the Server received a message, or if the client did. Â The way a lot of systems work is by using “ACK” messages, or “Acknowledgement Messages”. Â In military lingo, it’s customary to say “Roger” to confirm you heard, and understood the message the receiver sent via the 2 way radio. Â This prevents from them from having to send it again, and they can go onto do other things. Â For programs like FTP clients that have many steps, or even if you are downloading pieces of a file, you need to confirm you got the first message before the server will send the second. Â This also helps me debug if my parsing code works since I’ve yet to have a failure with a Chat Server and Client talking on localhost.
The PendingMessage sends Message VO’s on a specified MessageSocket. Â Most messages assume they’ll need an ACK sent back. Â ACK’s are confirmed by matchin ID’s since every message has a unique ID. Â The PendingMessage will also attempt to retry 3 times if a message times out and doesn’t get an ACK. Â I wanted to encapsulate all that insanity, and PendingMessage is it.
Before I learned about Sockets randomly sending 1 or 3 messages at a time, I thought I could “queue” things in a line; thus ensuring only 1 message was sent on a Socket at a time. Â Once I realized you couldn’t do that, I kept the Queue around anyway. Â It ensures that if you send a chat message, you wait for that message to get an ACK from the server before another message is sent. Â While TCP/IP and Socket IS setup for sending multiple messages at once, and batching them… you can run into situations where the Socket can get too much data if you send too many messages. Â Additionally, it’s easier to debug if only 1 message is going at a time, and you can quickly see what failed, where. Â The MessageQueue sends messages and ACK messages. Â It encapsulates creating a PendingMessage, waiting for it to succeed or fail, and then moving onto processing the next one.
I coded myself into a corner with MessageQueue. Â He was made dumb on purpose: 1. send a message 2. when it’s done, process the next one. Â The problem, though, was it couldn’t distinquish between a message, and an ACK message. Â Rather than making the class complicated, I just created a queue for both regular messages, and ACK messages, and abstract that via MessageManager; a Facade to both queues.
ChatServer – ChatServerService
The majority of code in the ChatServer AIR project is self-explanatory; a simple MVCS setup. Â The ChatServerService is the only class worth mentioning; he’s what uses the new AIR 2 functionality called ServerSocket. Â This class does 3 main things. Â First, it listens using the ServerSocket to incoming connections. Â Second, those Sockets that connect, it listens to their messages and responds accordingly. Â Third, it allows you to boot clients.
The second one is the crux of the work. Â If a client sends a chat message, it’ll send that message out to everyone currently connected (there is no “room” support). Â When a new client connects and updates their user name, it’ll update everyone’s current list of connected user names; same with disconnects.
ChatClient – ChatService
This class connects to the server, negotiates the connection routine, and sends chat messages. Â Pretty straightforward: listen for Socket messages, and dispatch the important ones as events.
Other Lessons Learned
Networking ins’t really my forte. Â I’ve had no formal training. Â So a lot of the terms, techniques, and networking topographies I had to learn over the years on my own and from IT co-workers. Â Some of it isn’t very thorough, but it’s enough to get the ChatServer up and running.
For example, “localhost” and 192.168.0.1 and 127.0.0.1 are often the same thing; your computer from a networking perspective. Â The 192 address is often what your router assigns you if you in an internal home network. Â The localhost is the string name, whereas 127.0.0.1 is the numerical one. Â In the case of ServerSocket and Socket, all 3 can be used, and all 3 work. Â Granted, 192 won’t be your IP address if you aren’t behind a consumer router. Â If you plug directly to the internet, your IP will often be assigned by your hosting provider. Â This may not be “static”, but often is the same for a long time if you never disconnect from the internet for an extended period of time. Â This’ll obviously be unique if your on a public network, such as using the wireless at Starbucks. Â They often have a router that’ll assign you a temporary IP address. This all assumes DHCP is on, which allows the router to assign you a random IP address vs. a fixed one (like I do with my Tivo and XBox; that way I always know what their address is).
If you’re trying to run the chat server, you’ll need to know your “public” IP, or your address on the internet. Â I use “whatismyip.com“. Â You could also open a Command Prompt on Windows and type “ipconfig -all” …or was it -a… anyway, if you do that, it’ll list your public IP that your router is using if you’re behind one, else it’ll show your real, assigned IP. Â I don’t know the Terminal command on Mac, so I just use the website.
If you ARE using a router, while clients can connect to your public IP, if your router blocks the port (8087 in this case), it won’t work. Â Additionally, they’ll be “connecting” to your router, not your computer which is running the ChatServer AIR 2 app. Â You’ll have to do some port forwarding to basically tell your router, “Hey dude, when anyone tries to connection over port 8087, please allow this AND forward that request to my computer, which is 192.168.0.x, thank you!”. Â Once I did that, my ChatServer started working for people.
…none of this crap is required for the ChatClient. Â Again, he can work if he’s in a web browser, it’s just don’t have to deal with Flash Player’s annoying security if you just use an AIR application.
I’ll try to keep my server open for awhile if you want to chat and/or ask questions about the code. Â You can either install the ChatClient.air file I provided, or compile it yourself, and then type in this IP address: 126.96.36.199
Otherwise, feel free to create your own chat rooms!
Yes, I know I could of used Flex 4 and released this stuff a week early, but a lot of people have Flash Builder 4 Beta 2 builds that have expired, me included. Â I like to release code that… you know… works.
I’m really excited about the ServerSocket functionality in AIR 2. Â It’s my favorite feature. Â With Flash Player 10, AIR 2, and LCCS, and Flash Media Server 3.5, we now have multiple ways to build real-time applications, over a variety of protocols (TCP/IP, UDP, IP Multicast, etc.) and bandwidth distribution scenarios (server(s) to client(s), peer to peer, and client/server to client/server via ServerSocket). Â It’s nice to finally have a way to cheaply build networked Flash applications without the need of a true server with middle-ware.
Since it’s an AIR application, you can actually have the application also be the server, which opens up a lot of application opportunities. Â If the market would take a breather from video, I might actually get the opportunity to get clients with opportunities thats right up ServerSocket’s alley!
The code is under a Creative Commons Attribution 3.
Chat Client and Server by Jesse Warden is licensed under a Creative Commons Attribution 3.0 United States License.
Based on a work at jessewarden.com.
Source Code – ZIP
AIR 2 Beta – Download the Runtime, not the SDK
Chat Client Install (or you can download the AIR file)
Chat Server Install (or you can download the AIR file)