I always wanted to be able to create an AIR app that could listen for socket connections and act as a server. In the previous versions of Adobe AIR, you needed a third party socket server (written in PHP or other server-side technologies) in order to exchange data between 2 or more AIR applications. Today, thanks to the ServerSocket class available in AIR 2.0, it is possible to create AIR applications that listens for socket connections directly from ActionScript 3.0.

Time ago I created a cool little chat AIR application that used a SQLite database to synchronize messages and presence statuses between clients. The main purpose for this application, was to provide the ability to chat over a network, without the need of an internet connection. The idea was simple and at the same time complicated to implement: each chat client installed on a network computer, had to connect to a SQLite database dropped on a shared location on the same network. Each client does periodic queries and updates to the database to send presence statuses, read other clients presence statuses, read and send IM and so on. I have to say that the sync was working pretty good, however, I wouldn't recommend it for large networks with 100+ users, as SQLite is not meant for heavy multi-user duty.

Now I would definitely switch of a new version of InChat using sockets instead of a centralized database engine. I would need to create a server app that listens for clients connections and provide data exchange to them.

So I've been playing around with sockets in AIR 2.0 two days ago, and I could let two apps send and receive data back and forth easily. Let me explain how. Basically, you will need the new AIR 2.0 runtime and SDK that you can find here (in case you didn't install them yet). Once you install the runtime and prepare the SDK, you will need to do two apps. The server app and the client app.

The Server Application

First things first, in your Server.as class, you need two class variables. The serverSocket instance is what we will use to open a port and listen for other clients' connections, while the clientSocket instance, will be used to handle client data:

private var serverSocket:ServerSocket;
private var clientSocket:Socket;

Now we need to open a port and to start listening for connections. In my case I set the port 8888 because on Mac OS X this port does not require a super user authentication. However whether on Windows or Mac, you can choose any port you want as long as it is not opened by another application.

The function below should be called when you click on an "Open connection" button for example. As you can see it is adding a listener that will dispatch when "someone" connects to it:

private function connect(e:MouseEvent):void {
    serverSocket = new ServerSocket();
    serverSocket.addEventListener(ServerSocketConnectEvent.CONNECT, onConnect);
    serverSocket.bind(8888);
	serverSocket.listen();
}

Once a client connects to the serverSocket instance the ServerSocketConnectEvent even dispatches and it is handled:

private function onConnect(e:ServerSocketConnectEvent):void {
    Alert.show("Client is connected");
    clientSocket = e.socket;
    clientSocket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
    clientSocket.addEventListener(Event.CLOSE, onConnectionClosed);
}

The ServerSocketConnectEvent, provides an instance of a Socket object. What is happening here is I'm keeping a reference of it in the clientSocket variable created previously. The clientSocket instance is listening for two events, one that is dispatched when it sends data, and one when its connection is no longer available.

Now let's handle these events:

private function onData(e: ProgressEvent):void {
    var s:String = clientSocket.readObject();
    Alert.show(s);
}

private function onConnectionClosed(e:Event):void {
    Alert.show("Client Socket is Closed");
}

The onData function, will show an alert box when something is sent from the client. The readObject() method returns a serialized AMF object that can be whatever object you need. You can use readUTFBytes() for simple strings.
Finally, the onConnectionClosed function, will notify that the client has been disconnected (i.e.: the application is closed).

The Client Application

In the Client.as class you would need to connect to the Server application by using just a Socket instance:

private var socket:Socket;

private function connectToServer(event:MouseEvent):void {
    socket = new Socket();
    socket.addEventListener(Event.CONNECT, onConnect);
    socket.connect("127.0.0.1", 8888);
}

In this example, I'm connecting to port 8888 on 127.0.0.1 (locahost), because the server app and the client app will run on the same computer, but you can run the server on another computer on the same network if you want. I also added an event listener that will dispatch once the connection to the server is established. Now we need to handle that event:

private function onConnect(e:Event):void {
Alert.show("Connected to server");
    socket.addEventListener(Event.CLOSE, onClose);
}

Once we established a connection to the server, the Alert box will do its job and inform the user, then we would need to add an event listener that will dispatch when the server disconnects. Now we need to handle that and send some data to the server:

private function onClose(e:Event):void {
    Alert.show("Server Socket is Closed");
}

private function sendToServer(e:MouseEvent):void {
     socket.writeObject("Hello Server");
}

As you can see in the sendToServer function, I am sending or "writing" an object (in this case a string) to the server. The server's onData function that we have written in Server.as will handle it and show the message in an Alert box. Of course you can use socket.writeUTFBytes("Hello Server"); but then again, you will need to readUTFBytes(); on the server to read the message.

Now run both Server and Client and try them out. You can find the source code for this example here.