Bidirectional LocalConnections in Actionscript 3 (Flex 2 / Flex 3 / Flash CS3)

Although I use AMFPHP RemoteObjects with the Cairngorm Framework everyday, I never had a need for a simple LocalConnection. LocalConnections let you communicate between running SWFs, the only problem is that they are unidirectional. SWF A can make a new LocalConnection to SWF B and invoke it’s methods, but SWF B can’t contact SWF A. The way to get around this is to make another LocalConnection back from SWF B to SWF A. Trying to wrap my head around receiving and sending connections was starting to make me angry! They are annoyingly misleading - the receiving SWF needs to .connect() to a named connection, whereas the sending SWF doesn’t - it just calls .send() with the same named connection. I figured I could do the world a favor and abstract this confusion for you.

Here is the result (demonstrated with a Flex SWF and a Flash SWF):

Note: this post is over 10 years old and the demos don’t work in modern browsers!

<script src="/Scripts/AC_RunActiveContent.js" type="text/javascript"></script>

Flex SWF

<script type="text/javascript">// <![CDATA[
  AC_FL_RunContent( 'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0','width','300','height','300','src','/steve_blog_content/BiDirLocalConnection/LCTest_flex','quality','high','pluginspage','http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash','movie','/steve_blog_content/BiDirLocalConnection/LCTest_flex' ); //end AC code
// ]]></script>
<noscript>
<span class="mceItemObject"  classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="300" height="300"><span  name="movie" value="/steve_blog_content/BiDirLocalConnection/LCTest_flex.swf" class="mceItemParam"></span><span  name="quality" value="high" class="mceItemParam"></span><span class="mceItemEmbed"  src="/steve_blog_content/BiDirLocalConnection/LCTest_flex.swf" quality="high" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="300" height="300"></span></span>
</noscript>

Flash SWF

<script type="text/javascript">// <![CDATA[
  AC_FL_RunContent( 'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0','width','300','height','300','src','/steve_blog_content/BiDirLocalConnection/LCTest_flash','quality','high','pluginspage','http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash','movie','/steve_blog_content/BiDirLocalConnection/LCTest_flash' ); //end AC code
// ]]></script>
<noscript>
<span class="mceItemObject"  classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="300" height="300"><span  name="movie" value="/steve_blog_content/BiDirLocalConnection/LCTest_flash.swf" class="mceItemParam"></span><span  name="quality" value="high" class="mceItemParam"></span><span class="mceItemEmbed"  src="/steve_blog_content/BiDirLocalConnection/LCTest_flash.swf" quality="high" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="300" height="300"></span></span>
</noscript>

In both the Flex MXML and the Flash FLA, I am including my BiDirLocalConnection class and it does all the hard work for me.

To make this bidirectional concept easier to handle, I introduced something called Roles. There are two roles available to your SWFs: Master and Slave. You must pick one of these roles when you instantiate the BiDirLocalConnection Object. If you have more than one running SWF in a given Role you will get an error on the newest one.

Here’s an example of how to use the class in Flex:

import net.teratechnologies.common.BiDirLocalConnection;

private var connection:BiDirLocalConnection;

private function init():void{
    connection = new BiDirLocalConnection(BiDirLocalConnection.ROLE_MASTER,this);
    connection.connect();
}

The constructor takes three arguments (the third is optional):

BiDirLocalConnection(role:String,callbackScope:Object,connectionBaseName:String="BiDirConnection")
  • role: The role of this SWF (ROLE_MASTER or ROLE_SLAVE)
  • callbackScope: When a SWF connects to this one and tries to invoke a method (call a function), where should it look to find the method? Normally you specify this so the remote SWF has access to the functions in your current scope.
  • connectionBaseName: This optional argument is only required if you have more than one BiDirLocalConnection at the same time. You can use any string here and it will serve as the prefix for the two LocalConnection connectionNames that are used.

Here is the complete code from the Slave in Flash CS3 as seen above. There are three things on the stage: TextArea (receiveText), TextInput (sendText) and a Button (sendButton):

import net.teratechnologies.common.BiDirLocalConnection;
			
var connection:BiDirLocalConnection;

function init():void{
	sendButton.addEventListener(MouseEvent.CLICK,send);
	connection = new BiDirLocalConnection(BiDirLocalConnection.ROLE_SLAVE,this);
	connection.connect();
}
function send(e:Event):void{
	connection.send('showText',sendText.text);
}
function showText(t:String):void{
	receiveText.text = "Received: "+t+"\n"+receiveText.text;
}

init();
stop();

Now here is the complete code for the Flex MXML file:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import net.teratechnologies.common.BiDirLocalConnection;
			
			private var connection:BiDirLocalConnection;
			
			private function init():void{
				sendButton.addEventListener(MouseEvent.CLICK,send);
				connection = new BiDirLocalConnection(BiDirLocalConnection.ROLE_MASTER,this);
				connection.connect();
			}
			private function send(e:Event):void{
				connection.send('showText',sendText.text);
			}
			public function showText(t:String):void{
				receiveText.text = "Received: "+t+"\n"+receiveText.text;
			}
			
		]]>
	</mx:Script>
	<mx:Panel width="100%" height="100%" layout="absolute" title="Flex 2/3">
		<mx:TextArea id="receiveText" right="10" bottom="39" left="10" top="10."/>
		<mx:TextInput id="sendText" left="10" right="72" bottom="10"/>
		<mx:Button label="Send" id="sendButton" right="10" bottom="10"/>
	</mx:Panel>
	
</mx:Application>

It really is quite simple, the SWFs are calling each other’s showText functions with the text of the TextInput box as an argument. You can pass as many arguments as you want to connection.send(), and they don’t need to be simple Strings.

Finally, here is the ActionScript Code for the BiDirLocalConnection class itself:

package net.teratechnologies.common {
	import flash.net.LocalConnection;
	
	public class BiDirLocalConnection{
		
		public static const ROLE_MASTER:String = "master";
		public static const ROLE_SLAVE:String = "slave";
		
		private var txConnName:String;
		private var rxConnName:String;
		private var txLC:LocalConnection = new LocalConnection();
		private var rxLC:LocalConnection = new LocalConnection();
		private var callbackScope:Object;
		
		public function BiDirLocalConnection(role:String,callbackScope:Object,connectionBaseName:String="BiDirConnection"){
			if(role == ROLE_MASTER){
				txConnName = connectionBaseName + "_TX";
				rxConnName = connectionBaseName + "_RX";
			}else{
				rxConnName = connectionBaseName + "_TX";
				txConnName = connectionBaseName + "_RX";
			}
			this.callbackScope = callbackScope;
		}
		public function connect():void{
			trace(rxConnName);
			rxLC.connect(rxConnName);
			rxLC.client = callbackScope;
		}
		public function send(methodName:String,...rest):void{
			txLC.send(txConnName,methodName,rest);
		}
	}
}

You can also download the Flex 2/3 file, Flash FLA and Class file in a ZIP file.

stevekamerman

COO @scientiamobile