Zoo2006-10-07_043Kuortti.2010-06-26Jodo-Milano.2008-09_035Saaristoleiri-Uto-2008_177italia2005-05_131playa06secret_552Nanbudo_WCh2006_076Kuortti.2010-06-26Pakkas03Pitkanen_076Kuortti.2010-06-26Helsinki2005-10-15_16_042Slovenia2009-04_658Oslo.Norway.2002.summer.034South.Africa.Roadtrip.2007-08_353South.Africa.Roadtrip.2007-08_216italia2005-04_219Osaka, Japan 2010-04-29Kobe, Japan 2010-05-01Saaristoleiri 2009-08 34Saaristoleiri-Uto-2008_180Vesa-Rauttu-60v_1899Shiga, Japan 2010-04-18
Liquid bronze pooring down

Rather hot bronze soon to form a shape again

XIFF chat - Part 7: Compression

The first compression method provided within the XMPP extensions is Zlib, which was recently lifted to the final standard status. Indeed TLS has compression methods available, but it is not supported currently in XIFF.

Compression is not actually that difficult, Flash player does it natively via ByteArray. Also the feature has been available now for few months in XIFF trunk. But it has not been working properly as there are some differences between the binary output from other XMPP clients and Flash player. Most probably the data from ByteArray would need some additional headers, but I had so far no time to investigate in to this.

There also exists another compression method, LZW, but it is in draft status and it is not widely supported on the server side and nowhere in the client side. Yet.

Next thing is to get the full support of TLS in XIFF. It might be interesting to see what will come of Google Wave once it is complete. It is one of the many XMPP services which require using TLS, like Google Talk.

Below is the code to see the difference between compressed and uncompressed data amount.

/**
 * @mxmlc -target-player=10.0.0
 */
package
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;
    import flash.utils.*;
    import flash.ui.Keyboard;
    import flash.system.Security;

    import org.igniterealtime.xiff.core.*;
    import org.igniterealtime.xiff.auth.*;
    import org.igniterealtime.xiff.data.*;
    import org.igniterealtime.xiff.events.*;
    import org.igniterealtime.xiff.conference.*;
    import org.igniterealtime.xiff.data.*;
    import org.igniterealtime.xiff.im.*;
    import org.igniterealtime.xiff.data.browse.*;
    import org.igniterealtime.xiff.data.im.*;
    import org.igniterealtime.xiff.data.muc.*;
    import org.igniterealtime.xiff.data.vcard.*;

    [SWF(backgroundColor = '0x042836', frameRate = '33', width = '600', height = '400')]

    /**
     * A test to use the new compression feature in XIFF
     * with statistics comparison to non compressed.
     * @see http://paazio.nanbudo.fi/tutorials/flash/xiff-chat-part-7
     */
    public class XIFFstep7 extends Sprite
    {
        private const SERVER:String = "192.168.1.37";
        private const PORT:int = 5222;
        private const USERNAME:String = "flasher";
        private const PASSWORD:String = "flasher";
        private const RESOURCE_NAME:String = "Compression";
        private const CHECK_POLICY:Boolean = true;
        private const POLICY_PORT:uint = 5229;
        private const COMPRESS:Boolean = true;
        private const METHOD:uint = XMPPConnection.STREAM_TYPE_STANDARD;
       
        private var _connection:XMPPConnection;
        private var _roster:Roster;
        private var _keepAlive:Timer;
       
        private var _statField:TextField;
        private var _statTimer:Timer;
        private var _format:TextFormat = new TextFormat("Verdana", 12, 0xF4F4F4);
       
        private var outgoingNonCompressed:uint = 0;
        private var incomingNonCompressed:uint = 0;

        public function XIFFstep7()
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
           
            loaderInfo.addEventListener(Event.INIT, onInit);
        }
       
        private function onInit(event:Event):void
        {
            if (CHECK_POLICY)
            {
                Security.loadPolicyFile("xmlsocket://" + SERVER + ":" + POLICY_PORT);
            }
            initChat();
        }

        private function initChat():void
        {
            _statField = createField("statField", 2, 2, 300, 70);
            _statField.defaultTextFormat = _format;
            addChild(_statField);
           
            _statTimer = new Timer(2 * 1000);
            _statTimer.addEventListener(TimerEvent.TIMER, onStatTimer);
            _statTimer.start();
           
            createConnection();
           
            _roster = new Roster(_connection);
            _roster.addEventListener(RosterEvent.ROSTER_LOADED, onRoster);
           
            _connection.connect(METHOD);
           
            _keepAlive = new Timer(2 * 60 * 1000); // 2 minutes
            _keepAlive.addEventListener(TimerEvent.TIMER, onKeepAliveLoop);
        }
       
        private function createConnection():void
        {
            _connection = new XMPPConnection();
            _connection.addEventListener(DisconnectionEvent.DISCONNECT, onDisconnect);
            _connection.addEventListener(XIFFErrorEvent.XIFF_ERROR, onXiffError);
            _connection.addEventListener(IncomingDataEvent.INCOMING_DATA, onIncomingData);
            _connection.addEventListener(LoginEvent.LOGIN, onLogin);
            _connection.addEventListener(MessageEvent.MESSAGE, onMessage);
            _connection.addEventListener(OutgoingDataEvent.OUTGOING_DATA, onOutgoingData);
            _connection.addEventListener(PresenceEvent.PRESENCE, onPresence);
           
            _connection.server = SERVER;
            _connection.username = USERNAME;
            _connection.password = PASSWORD;
            _connection.port = PORT;
            _connection.resource = RESOURCE_NAME;
            _connection.compress = COMPRESS;
        }
       
        private function onDisconnect(event:DisconnectionEvent):void
        {
            trace("onDisconnect. " + event.toString());
            _keepAlive.stop();
        }
       
        private function onXiffError(event:XIFFErrorEvent):void
        {
            trace("onXiffError. " + event.toString());
            trace("onXiffError. errorMessage: " + event.errorMessage);
        }
       
        private function onIncomingData(event:IncomingDataEvent):void
        {
            trace("onIncomingData. " + event.toString());
            incomingNonCompressed += event.data.length;
        }
       
        private function onOutgoingData(event:OutgoingDataEvent):void
        {
            trace("onOutgoingData. " + event.toString());
            outgoingNonCompressed += event.data.length;
        }
       
        private function onLogin(event:LoginEvent):void
        {
            trace("onLogin. " + event.toString());
           
            var presence:Presence = new Presence(null, _connection.jid.escaped);
            _connection.send(presence);

            _keepAlive.start();
        }
       
        private function onMessage(event:MessageEvent):void
        {
            trace("onMessage. " + event.toString());
            trace("onMessage. time: " + event.data.time);
        }
       
        private function onPresence(event:PresenceEvent):void
        {
            trace("onPresence. " + event.toString());
        }

        private function onKeepAliveLoop(event:TimerEvent):void
        {
            _connection.sendKeepAlive();
        }
       
        private function onStatTimer(event:TimerEvent):void
        {
            if (_connection != null)
            {
                _statField.text = "Compressed / Uncompressed\n"
                    + "Incoming KB: " + Math.round(_connection.incomingBytes / 1024) + " / "
                    + Math.round(incomingNonCompressed / 1024) + "\n"
                    + "Outgoing KB: " + Math.round(_connection.outgoingBytes / 1024) + " / "
                    + Math.round(outgoingNonCompressed / 1024) + "\n";
            }
        }
           
        private function onRoster(event:RosterEvent):void
        {
            trace("onRoster. " + event.toString());
            trace("onRoster. data: " + event.data);
            switch (event.type)
            {
                case RosterEvent.ROSTER_LOADED :
                    break;
            }
        }
       
        private function createField(name:String, xPos:Number,
            yPos:Number, w:Number, h:Number):TextField
        {
            var format:TextFormat = new TextFormat("Verdana", 12, 0x121212);
            var bgColor:uint = 0xE3E3C9;
            var borderColor:uint = 0x961D18;
           
            var field:TextField = new TextField();
            field.name = name;
            field.defaultTextFormat = format;
            field.background = true;
            field.backgroundColor = bgColor;
            field.border = true;
            field.borderColor = borderColor;
            field.multiline = true;
            field.wordWrap = true;
            field.mouseWheelEnabled = true;
            field.x = xPos;
            field.y = yPos;
            field.width = w;
            field.height = h;
           
            return field;
        }
    }
}

Please use the Igniterealtime community forums to discuss of this feature.

Time: 17/12/2009 12:36

QR code for paazio.nanbudo.fi