LocalBroadcaster – multiple LocalConnections

The LocalConnection class lets you create a LocalConnection object that can invoke a method in another LocalConnection object. (adobe)

LocalBroadcaster

LocalBroadcaster

The problem is, LocalConnection allows you to communicate only between two .swf files. The LocalBroadcaster class lets you create multiuser LocalConnection-s. Post includes:

This is how it works:

  1. .swf 1 creates connection to LocalConnection
  2. after success .swf 1 saves its connection id to persistance (shared object suits just fine)
  3. .swf 2 creates connection to LocalConnection
  4. after success .swf 2 saves its connection id to persistance
  5. .swf 3 creates connection to LocalConnection
  6. after success .swf 3 saves its connection id to persistance
  7. .swf 1 broadcasting message, ask for all connections ids from persistance
  8. .swf 1 sends message over LocalConnection to all clients
  9. .swf 2 receives message from LocalConnection
  10. .swf 3 receives message from LocalConnection
LocalBroadcaster

LocalBroadcaster

sk.yoz.net.LocalBroadcaster

package sk.yoz.net
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.StatusEvent;
    import flash.net.LocalConnection;
    import flash.net.SharedObject;
    
    import sk.yoz.events.LocalBroadcasterEvent;
    
    public class LocalBroadcaster extends EventDispatcher
    {
        private var connection:LocalConnection;
        private var _channel:String;
        private var so:SharedObject = SharedObject.getLocal("LocalBroadcaster", "/");
        private var senderList:Object;
        
        public static const HALLO_WORLD_MESSAGE:String = ".,*-@Hallo World@-*,.";
        
        private var connectionList:Array = new Array();
        
        public function LocalBroadcaster()
        {
            super();
            var result:Boolean;
            var attempts:uint = 0;
            while(!result || attempts >= 5)
            {
                result = initConnection(randomChannel);
                attempts++;
            }
            
            if(result)
                addChannel(public::channel);
            else
                trace("LocalBroadcaster: Unable to create connection!");
        }
        
        private function get randomChannel():String
        {
            return Math.round(Math.random() * uint.MAX_VALUE).toString(36);
        }
        
        public function get channel():String
        {
            return _channel;
        }
        
        [Bindable(event="channelsChanged")]
        public function get channels():Array
        {
            var list:Array = [];
            if(!so || !so.data.hasOwnProperty("channels"))
                return list;
            for(var channel:String in so.data.channels)
                list.push(channel);
            return list;
        }
        
        private function set channel(value:String):void
        {
            _channel = value;
        }
        
        private function initConnection(channel:String):Boolean
        {
            connection = new LocalConnection();
            connection.client = this;
            try
            {
                connection.connect(channel);
            }
            catch(error:Error)
            {
                //dispatchEvent(new Error(error.message));
                connection = null;
                return false;
            }
            
            private::channel = channel;
            dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.CHANNEL_CREATED, false, false, channel));
            dispatchEvent(new Event("channelsChanged"));
            broadcast(HALLO_WORLD_MESSAGE);
            return true;
        }
        
        private function addChannel(channel:String):void
        {
            if(!so.data.channels)
                so.data.channels = {};
            if(so.data.channels.hasOwnProperty(channel))
                return;
            so.data.channels[channel] = channel;
            try
            {
                so.flush();
            }
            catch(error:Error){}
            dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.CHANNEL_ADDED, false, false, channel));
            dispatchEvent(new Event("channelsChanged"));
        }
        
        private function removeChannel(channel:String):void
        {
            if(!so.data.channels)
                return;
            delete so.data.channels[channel];
            try
            {
                so.flush();
            }
            catch(error:Error){}
            dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.CHANNEL_REMOVED, false, false, channel));
            dispatchEvent(new Event("channelsChanged"));
        }
        
        public function broadcast(data:Object, broadcastToSender:Boolean = false):void
        {
            var sender:LocalConnection;
            senderList = {};
            for(var channel:String in so.data.channels)
            {
                if(!broadcastToSender && channel == public::channel)
                    continue;
                sender = new LocalConnection();
                sender.addEventListener(StatusEvent.STATUS, onStatus);
                sender.send(channel, "receive", public::channel, data);
                senderList[channel] = sender;
                dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.CHANNEL_FOUND, false, false, channel));
                dispatchEvent(new Event("channelsChanged"));
            }
        }
        
        public function receive(channel:String, data:Object):void
        {
            if(data == HALLO_WORLD_MESSAGE)
            {
                addChannel(channel);
                dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.RECEIVED_HALLO_WORLD, false, false, channel, data));
                return;
            }
            
            dispatchEvent(new LocalBroadcasterEvent(LocalBroadcasterEvent.RECEIVED_DATA, false, false, channel, data));
        }
        
        private function onStatus(event:StatusEvent):void
        {
            if(!senderList)
                return;
                
            var sender:LocalConnection;
            var channel:String;
            for(var key:String in senderList)
                if(senderList[key] == event.target)
                    channel = key;
            
            senderList[channel] = null;
            delete senderList[channel];
            if(event.level == "error")
                removeChannel(channel);
        }
    }
}

sk.yoz.events.LocalBroadcasterEvent

package sk.yoz.events
{
    import flash.events.Event;

    public class LocalBroadcasterEvent extends Event
    {
        private var _channel:String;
        private var _data:Object;

        public static const RECEIVED_DATA:String = "LocalBroadcasterEventReceivedData";
        public static const RECEIVED_HALLO_WORLD:String = "LocalBroadcasterEventReceivedHalloWorld";

        public static const CHANNEL_ADDED:String = "LocalBroadcasterEventChannelAdded";
        public static const CHANNEL_CREATED:String = "LocalBroadcasterEventChannelCreated";
        public static const CHANNEL_FOUND:String = "LocalBroadcasterEventChannelFound";
        public static const CHANNEL_REMOVED:String = "LocalBroadcasterEventChannelRemoved";

        public function LocalBroadcasterEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false,
            channel:String=null, data:Object=null)
        {
            super(type, bubbles, cancelable);

            _channel = channel;
            _data = data;
        }

        public function get channel():String
        {
            return _channel;
        }

        public function get data():Object
        {
            return _data;
        }
    }
}

usage:

var localBroadcaster:LocalBroadcaster = new LocalBroadcaster();
localBroadcaster.addEventListener(LocalBroadcasterEvent.RECEIVED_DATA, localBroadcasterHandler);
localBroadcaster.broadcast("hallo", true);

function localBroadcasterHandler(event:LocalBroadcasterEvent):void
{
	trace(event.data);
}

7 comments so far

  1. Teenage November 25, 2010 14:40

    At the momment I’ve only found the asLocalConnect library to add LocalConnection broadcasting, typically synchronise mute buttons on multiples video/audio player, but you have to set registrations at build time : quite a static method…

    Your solution sound great : a mix between LocalConnection and shared object for dynamic registration seem perfect. I’ll post a comment if I successfully managed to use it on my website…

  2. Indro May 26, 2011 17:04

    It is a wonderful idea. I have a similar situation but

    Can you suggest a way by which I can clear the shared object when I try to close the window. I am trying to send a message to all the windows or tabs when I am closing a window/tab at a time but it does not work good when I have multiple tabs open in a single window and message is not sent to all the subscribers and there is some share object cookie remains when I open the window again having the swf.

    Example:

    1. Load an Application window with a swf 1.
    2. Load another application window in a tab with swf 1.
    3. I am closing the second tab, I clear itself from the cookie I send a message to my first application swf and it knows now that it is the single application opened. (works fine).
    4. Now intead of closing one rtab at time I try to close the window by pressing the close button of the window.
    5. The current application opened clears itself but the other application running in the other tab does not get the notification to clear itself.(Problem case)

    Please suggest what shall I do. I need to know a window whether it is the last application running on the cleint machine.

  3. Jozef Chúťka May 27, 2011 16:55

    Hi Indro,
    you should be able to handle LocalConnection status events when some LocalConnection responder fails: event.level = “error”… not sure any event is dispatched when flash closed (window closed) but if not you should be ready to ping the instances in an interval…

  4. Indro May 27, 2011 18:56

    Hi Jozef,

    Ur explanation gives me an idea of doing it but still not sure what will happen when we close a window having multiple tabs opened having the swf files running in them. How each swf file will know they have to de-register themselve from the shared object, So that the shared object gets cleared off when we close all the tabs(clicking the window close button)…. As there is a requirement in my current application when I need to do something with the current count of subscribers already registered in the shared object.

    The explanation which u have suggested about pinging after a fixed interval works when we have any instance of swf file running but what will happen when we try to close all in a single go.

    Need an immediate solution.. See if anybody come up with a possible solution..

  5. Adi June 10, 2011 04:18

    Thanks for this class, this is very useful 🙂

  6. Adi July 14, 2011 03:15

    Hi Jozef,

    I can’t seem to use your class for AIR to swf communication. Please suggest what shall I do to make this class work for AIR to swf communication. Thanks so much 🙂

  7. Jozef Chúťka July 14, 2011 10:57

Leave a comment

Please be polite and on topic. Your e-mail will never be published.