Socket.IO is an awesome library for NodeJS for making games and chat apps relying on WebSocket protocol. Web browsers supporting WebSockets at the time of this writing are Chrome, Safari and Firefox 4 beta, and Opera 10.70 beta Other browsers can use WebSocket over Flash or other methods (XHR Pooling, XHR Streaming, forever IFrame). In this article I will show you how I connected AIR app to Socket.IO-Node chat demo using WebSocket over Flash library made by Hiroshi Ichikawa.
Requirements:
- for the Server
- for the Server
- nodejs - NodeJS
- socket.IO-node - http://github.com/LearnBoost/Socket.IO-node
- for the Client
- FlashDevelop - http://www.flashdevelop.org/
- web-socket-js - http://github.com/gimite/web-socket-js
- 3rd party lib - https://github.com/mikechambers/as3corelib
UPDATE - github source -> here
This article is not about setting up server side. You can get enough information on NodeJS and Socket.io websites. I will show you how to connect AIR app to Socket.IO server. Create new AIR Flex 4 project in Flash Develop. Make some simple GUI layout and save it as FlexMain.mxml
This article is not about setting up server side. You can get enough information on NodeJS and Socket.io websites. I will show you how to connect AIR app to Socket.IO server. Create new AIR Flex 4 project in Flash Develop. Make some simple GUI layout and save it as FlexMain.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
backgroundColor="#FFFFFF"
backgroundAlpha="0"
creationComplete="init();">
<mx:VBox width="100%" height="500">
<mx:List id="messages" width="100%" height="400" dataProvider="{msgBuffer}" />
<mx:HBox>
<mx:TextInput id="msgbox" height="50" width="650" keyDown="msgboxKeyDown(event)" />
<mx:Button id="btn" click="sendMessage()" height="50" width="50" />
</mx:HBox>
</mx:VBox>
</s:WindowedApplication>
Get the web-socket-js latest version with Git subversion control system and copy WebSocket.as, WebSocketStateEvent.as and com folder to your project /src folder.
Get as3corelib from github and copy this folder as3corelib\src\com\adobe\serialization to your src\com\adobe folder in your project. It contains JSON classes for parsing, encoding and decoding. Now your \src folder have to looks like this
<fx:Script>
<![CDATA[
import flash.events.*;
import flash.net.*;
import flash.system.*;
import flash.utils.*;
import WebSocket;
import com.adobe.serialization.json.*;
import mx.collections.ArrayCollection;
[Bindable]
private var msgBuffer: ArrayCollection;
private var websock:WebSocket;
private var sessionId:String;
private var frame:String = '~m~';
private function init():void {
msgBuffer = new ArrayCollection([]);
sessionId = null;
loadPolicyFile("xmlsocket://192.168.1.16:843");
websock = new WebSocket(this, "ws://192.168.1.16:8080/socket.io/flashsocket","");
websock.addEventListener("open", onOpen);
websock.addEventListener("close", onClose);
websock.addEventListener("message", onMessage);
}
public function onMessage(e:Event):void
{
var msgs:Array = websock.readSocketData();
for each (var msg:String in msgs) {
msg = decodeURIComponent(msg);
log("received string: "+msg);
if (msg.substr(0, 3) == frame)
{
var m:Array = msg.split(frame);
if (sessionId == null) {
sessionId = m[2];
log("session id= " + sessionId);
}
else
{
log(" m[2] = " +m[2]);
if (m[2].substr(0, 3) == '~h~')
{
log("heartbeat: " + msg);
sendHeartBeat(m[2].substr(3));
}
else
if (m[2].substr(0, 3) == '~j~')
{
log("json: " + msg);
parseMsg(m[2].substr(3));
}
else
{
parseMsg(m[2]);
log ("message: " + m[2]);
}
}
}
}
}
private function parseMsg(s:String):void
{
var msg:Object = JSON.decode(s);
if (msg.announcement != null) {
trace("announcement :" + msg.announcement);
addToList(msg.announcement);
}
else
if (msg.message != null)
{
addToList(msg.message[0]+" :"+msg.message[1]);
}
else
if (msg.buffer != null) {
for each (var o:Object in msg.buffer) {
addToList(o.message[0]+" : "+o.message[1]);
}
}
}
private function addToList(s:String):void {
msgBuffer.addItem(s);
messages.verticalScrollPosition = messages.maxVerticalScrollPosition;
}
public function sendMsg(m:String):void
{
websock.send(frame + m.length + frame + m);
addToList("me : "+m);
}
public function sendHeartBeat(num:String):void
{
var strnum:int = 3 + num.length;
websock.send(frame + strnum.toString() + frame+ '~h~' + num);
}
public function onClose(e:Event):void
{
log("websocket disconnected");
}
public function onOpen(e:Event):void
{
log("websocket connected");
//websock.send(frame+'17'+frame+"USERNAME:username");
}
private function sendMessage():void {
sendMsg(msgbox.text);
msgbox.text="";
}
public function getCallerHost():String {
return ("http://192.168.1.16");
}
public function getOrigin():String {
return ("http://192.168.1.16");
}
public function fatal(str:String):void
{
trace(str);
}
public function error(str:String):void
{
trace(str);
}
public function log(str:String):void
{
trace(str);
}
public function loadPolicyFile(url:String):void
{
log("policy file: " + url);
Security.loadPolicyFile(url);
}
public function getCookie():String
{
return sessionId;
}
private function msgboxKeyDown(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.ENTER)
sendMessage();
}
]]>
</fx:Script>
In original WebSocket.as file the author use callback functions to main project
file to exchange log, errors and other messages. The name of the original
main project file is called WebSocketMain.as.To make it working with my flex app
I just named it to plain Object reference. So open WebSocket.as file and
search for WebSocketMain and replace it with Object
file to exchange log, errors and other messages. The name of the original
main project file is called WebSocketMain.as.To make it working with my flex app
I just named it to plain Object reference. So open WebSocket.as file and
search for WebSocketMain and replace it with Object
Its marked with big red bold font in the source.
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
// License: New BSD License
// Reference: http://dev.w3.org/html5/websockets/
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
package {
import flash.display.*;
import flash.events.*;
import flash.external.*;
import flash.net.*;
import flash.system.*;
import flash.utils.*;
import mx.core.*;
import mx.controls.*;
import mx.events.*;
import mx.utils.*;
import com.adobe.net.proxies.RFC2817Socket;
import com.hurlant.crypto.tls.TLSSocket;
import com.hurlant.crypto.tls.TLSConfig;
import com.hurlant.crypto.tls.TLSEngine;
import com.hurlant.crypto.tls.TLSSecurityParameters;
import com.gsolo.encryption.MD5;
[Event(name="message", type="flash.events.Event")]
[Event(name="open", type="flash.events.Event")]
[Event(name="close", type="flash.events.Event")]
[Event(name="error", type="flash.events.Event")]
[Event(name="stateChange", type="WebSocketStateEvent")]
public class WebSocket extends EventDispatcher {
private static var CONNECTING:int = 0;
private static var OPEN:int = 1;
private static var CLOSING:int = 2;
private static var CLOSED:int = 3;
private var rawSocket:Socket;
private var tlsSocket:TLSSocket;
private var tlsConfig:TLSConfig;
private var socket:Socket;
private var main:Object;
private var url:String;
private var scheme:String;
private var host:String;
private var port:uint;
private var path:String;
private var origin:String;
private var protocol:String;
private var buffer:ByteArray = new ByteArray();
private var dataQueue:Array;
private var headerState:int = 0;
private var readyState:int = CONNECTING;
private var bufferedAmount:int = 0;
private var headers:String;
private var noiseChars:Array;
private var expectedDigest:String;
public function WebSocket(
main:Object, url:String, protocol:String,
proxyHost:String = null, proxyPort:int = 0,
headers:String = null) {
Save it and you can compile the AIR app.
To test it you need to run the socket.io-node example server. It’s in the folder example. Go there and run it with # node server.js command.
Set your correct network location to the server in my case it’s 192.158.1.16. change it with your server domain, ip address or localhost. The default port of socket.io-node’s example is 8080. but u can change it if you have your own socket-io based server.
This is the final result.
thanks for reading!