Local Connection [en Anglais]

Dernière modification le août 6, 2010 | Imprimer

Local Connection enables communication between standalone SWF files on the same web page (or not, hence our dilemma; read on). So it is a very popular feature when it comes to synchronising different ad banners on the same page for the same execution (eg, a takeover). There are lots of useful tutorials on the web that will show you how to use it.

However, this guide is mainly concerned with an often overlooked issue when using Local Connection and one that is important for the successful delivery of ad banner executions that utilise Local Connection. Local Connections between SWF files are established in the scope of a user’s computer, not the web page they’re on or even within the browser. So there is ample room for inadvertently loading multiple instances of the same set of ad banners (same set of SWFs) all fighting to establish the same connections with the same names. The end result is unpredictable but inevitably some will not work. And throw in a Replay button, as you would for a page takeover with bells and whistles that is typical for something that would use Local Connection, then it’s anybody’s guess as to which set of ads will work and very likely crossfire-connections will be established so one ad banner will work with banners from another page and no execution on any single page quite work.

The Fix

Obviously we need to rein in the scope of any Local Connection made by ad banners on a web page from the user’s computer to that web page. We will do this by assigning a unique ID to connections that are made on the same web page.

Observe the example below:

You need Flash Player 8 or above and Javascript enabled on your browser to view this demo.

  • The three SWF files above are synchronised with Local Connection to make the arrow look like it’s travelling from one purple square to the next in an infinite cycle
  • Mouse over one of them to see its connection name; click to see the connection names on the other two
  • Use the ‘Reload’ link above to load the three SWF files again and observe the change in connection names
  • Use the ‘Open’ link above to open this page in a new browser window and observe the change in connection names, and more importantly, that the SWF files on the new page are working as expected. In fact, open as many copies as you like in the same or another browser, everything should still work
  • Problem solved!

Download AS3 | AS2 source files for this example

The Details

Notice that the first SWF always has the connection name masterXXXXXX, the second slave0_XXXXXX and the third slave1_XXXXXX. The XXXXXX is a numerical ID that is common to the connection names for all three SWF files on a page but is also unique to that page. The ID changes when you reload the SWF files and when you open another instance of the page. This ID is the key to the solution.

In a nutshell, this ID is a random number generated in the master SWF, and is then passed onto its slave SWF files. Take the example on this page, master SWF loads, generates a random number and tries to send this number to its slaves in intervals. At the same time, when slave0 and slave1 load, they establish temporary Local Connections to grab this random number at the first opportunity. Then they close the temporary connections and establish new ones with names containing the random number.

The assumption made here is that ad banners on the same page will always load together before another set of the same ad banners have time to load elsewhere. So the process described above has time to complete without interference, as when the slaves have temporary connections they’re vulnerable to other masters who are still rounding up their slaves. This process is extremely rapid and the same ad banners always have the same file sizes wherever they appear, so the assumption usually is always correct as it’s rare for two sets of ad banners rendering simultaneously.

Once the random numerical ID is safely passed on then all is safe and well. However, one word more on the master-slave relationship. The master is aware of its slaves and vice versa, but slaves are in the dark about each other. This needs to enforced by you, the code author, as there’s nothing stopping you from calling a slave from another one. But things can get messy that way and it’s best to always go through the master. This analogy also fails when you consider that the slave can get the master to do whatever they bid, so please don’t take the analogy too seriously.

Adapting the Example Files

AS3

What is explained in the previous section is all taken care of behind the scene in AS3, so implementation in your own FLAs should be straightforward once you have copied the AS library (the com folder) into your work directory.

In the master FLA, first establish the connection:

import com.yahoo.adtech.YLocalConnection.YLocalConnectionMaster;
import com.yahoo.adtech.YLocalConnection.YLCEvent;

stop();

var lc = new YLocalConnectionMaster(2, this); //Master LC object with 2 slaves
lc.makeConnection();

Then create a listener for when the master has made connections with all the slaves so you know when to initiate everything:

lc.addEventListener(YLCEvent.ALL_CONNECTED, allConnectedHandler);

function allConnectedHandler(e:YLCEvent):void
{
	//insert initiating code here, eg, play();
}

After this point in the code you can define functions that will be accessible for the slaves to call.

In a slave FLA, similarly begin by establishing the connection:

import com.yahoo.adtech.YLocalConnection.YLocalConnectionSlave;

stop();

var lc = new YLocalConnectionSlave(0, this); //Slave LC object with number 0
lc.makeConnection();

Ensure that the slave number (the first parameter when you instantiate a YLocalConnectionSlave) starts at 0 for the first slave and increment by 1 for each subsequent slave.

After this point in the code you can define functions that will be accessible for the master to call.

The master can call a method in one particular slave or call the same method in all slaves:

lc.sendToSlave(1, saySomething, 'hello world'); //calls the method saySomething() in slave 1
lc.sendToAllSlaves(saySomething, 'hello world'); //calls the method saySomething() in all slaves

A slave can only call a method in the master. It cannot communicate with another slave directly.

lc.sendToMaster(saySomething, 'hello world');

AS2

The FLA files for the AS2 example can pretty much be used unchanged. Well relatively. You will only need code on Frame 1 on the AS layer in all three FLA files.

In the master FLA, you only need to define the total number of slave it owns:

var TOTAL_SLAVES = 2;

Your master can have any number of slaves although we recommend up to 3 and certainly no more than 7.

You also need to define the functions the slaves can call over the connection, but leaving the masterConnection.confirmRetrieval() and masterConnection.closeAllConnections() functions intact:

function setLCMethods() {
	masterConnection.confirmRetrieval = function() {
		connectionsMade++;
	};

	masterConnection.closeAllConnections = function() {
		for (i=0; i<TOTAL_SLAVES; i++) {
			masterConnection.send(slaveIds[i], "closeConnection");
		};
		masterConnection.close();
	};

	//Change the below LCMethods to suit your needs
	masterConnection.go_play = function(frame) {
		_root.gotoAndPlay(frame);
	};

	//...
}

The init() function can then be modified and is called when the master has rounded up all its slaves.

For each slave FLA, it’s quite similar. First define the slave number (0, 1, 2, etc):

var SLAVE_NUMBER = 0;

Then define the functions the master can call over the connection inside setLCMethods(), leaving slaveConnection.connectNow() and slaveConnection.closeConnection() functions intact:

function setLCMethods(){
	slaveConnection.connectNow = function(uniqNum){
		uniqueNumber = uniqNum;

		masterId = "master" + uniqueNumber;
		slaveId = "slave" + SLAVE_NUMBER + "_" + uniqueNumber;

		slaveConnection.close();

		slaveConnection = new LocalConnection();
		slaveConnection.connect(slaveId);

		setLCMethods();

		slaveConnection.send(masterId, "confirmRetrieval");
	};

	slaveConnection.closeConnection = function(){
		slaveConnection.close();
	};

	//Change the below LCMethods to suit your needs
	slaveConnection.go_play = function(frame){
		_root.gotoAndPlay(frame);
	};

	//...
}

At any time when the master need to call a slave, just call:

masterConnection.send(slaveIds[SLAVE_NUMBER_HERE], "FUNCTION_NAME_HERE");

The other way around, call:

slaveConnection.send(masterId, "FUNCTION_NAME_HERE");

Always close() Your Connections

The master also has the built-in method closeAllConnections() that when called closes all the slave connections before closing itself. It is important that this is always called when the last synchronisation need has been met.

As well as being a good practice, closing unused local connections is the only effective way to minimise the impact of a bug on Macs where only 8 local connections can be made on a machine (documented since at least 2005). Beyond that connections simply fail in silence.

You can replicate this problem by opening multiple copies of this page on a Mac machine – usually on the third or fourth copy the arrow does not appear because your Mac has exceeded its local connection ration! Restarting your browser will renew the ration though.

A hack could be running a timeout function to check if the banners have kickstarted as expected after say five seconds. If not, then the banners could just start without synchronisation.