Building real time networked games and applications can be challenging. This tutorial will show you how to connect flash clients using Cirrus, and introduce you to some vital techniques.
Let’s take a look at the final result we will be working towards. Click the start button in the SWF above to create a ‘sending’ version of the application. Open this tutorial again in a second browser window, copy the nearId from the first window into the textbox, and then click Start to create a ‘receiving’ version of the application.
In the ‘receiving’ version, you’ll see two rotating needles: one red, one blue. The blue needle is rotating of its own accord, at a steady rate of 90°/second. The red needle rotates to match the angle sent out by the ‘sending’ version.
(If the red needle seems particularly laggy, try moving the browser windows so that you can see both SWFs at once. Flash Player runs EnterFrame events at a much lower rate when the browser window is in the background, so the ‘sending’ window transmits the new angle much less frequently.)
Step 1: Getting Started
First things first: you need a Cirrus ‘developer key’, which can be obtained at the Adobe Labs site. This is a text string that is uniquely assigned to you on registration. You will use this in all the programs you write to get access to the service, so it might be best to define it as a constant in one of your AS files, like this:
public static const CIRRUS_KEY:String = "<my string here>";
Note that its each developer or development team that needs its own key, not each user of whatever applications you create.
Step 2: Connecting to the Cirrus Service
We begin by creating a network connection using an instance of the (you guessed it) NetConnection class. This is achieved by calling the connect() method with your previously mentioned key, and the URL of a Cirrus ‘rendezvous’ server. Since at the time of writing Cirrus uses a closed protocol there is only one such server; its address is rtmfp://p2p.rtmfp.net
public class Cirrus
{
public static const CIRRUS_KEY:String = "<my string here>"
private static var netConnection:NetConnection;
public static function Init(key:String):void
{
if( netConnection != null ) return;
netConnection = new NetConnection();
try { netConnection.connect("rtmfp://p2p.rtmfp.net", key); }
catch(e:Error) {}
}
}
Since nothing happens instantly in network communication, the netConnection object will let you know what it’s doing by firing events, specifically the NetStatusEvent. The important information is held in the code property of the event’s info object.
private function OnStatus(e:NetStatusEvent):void
{
switch(e.info.code) {
case "NetConnection.Connect.Success": break; //The connection attempt succeeded.
case "NetConnection.Connect.Closed": break; //The connection was closed successfully.
case "NetConnection.Connect.Failed": break; //The connection attempt failed.
}
}
An unsuccessful connection attempt is usually due to certain ports being blocked by a firewall. If this is the case, you have no choice but to report the failure to the user, as they won’t be connecting to anyone until the situation changes. Success, on the other hand, rewards you with your very own nearID. This is a string property of the NetConnection object that represents that particular NetConnection, on that particular Flash Player, on that particular computer. No other NetConnection object in the world will have the same nearID.
The nearID is like your own personal phone number – people who want to talk to you will need to know it. The reverse is also true: you will not be able to connect to anyone else without knowing their nearID. When you supply someone else with your nearID, they will use it as a farID: the farID is the ID of the client that you are trying to connect to. If someone else gives you their nearID, you can use it as a farID to connect to them. Get it?
So all we have to do is connect to a client and ask them for their nearID, and then… oh wait. How do we find out their nearID (to use as our farID) if we’re not connected to each other in the first place? The answer, which you’ll be suprised to hear, is that it’s impossible. You need some kind of third-party service to swap the ids over. Examples would be:
- Building a server application to act as a ‘lobby’
- Emailing, or instant-messaging, your nearID to someone else
- Cooking something up using
NetGroups, which we might look at in a future tutorial
Step 3: Using Streams
The network connection is purely conceptual and doesn’t help us much after the connection has been set up. To actually transfer data from one end of the connection to another we use NetStream objects. If a network connection can be thought of as building a railway between two cities, then a NetStream is a mail train that carrys actual messages down the track.
NetStreams are one-directional. Once created they act as either a Publisher (sending information), or a Subscriber (receiving information). If you want a single client to both send and receive information over a connection, you will therefore need two NetStreams in each client. Once created a NetStream can do fancy things like stream audio and video, but in this tutorial we will stick with simple data.
If, and only if, we recieve a NetStatusEvent from the NetConnection with a code of NetConnection.Connect.Success, we can create a NetStream object for that connection. For a publisher, first construct the stream using a reference to the netConnection object we just created, and the special pre-defined value. Second, call publish() on the stream and give it a name. The name can be anything you like, it’s just there for a subscriber to differentiate between multiple streams coming from the same client.
var ns:NetStream = new NetStream(netConnection, NetStream.DIRECT_CONNECTIONS); ns.publish(name, null);
To create a subscriber, you again pass the netConnection object into the constructor, but this time you also pass the farID of the client you want to connect to. Secondly, call play() with the name of the stream that corresponds to the name of the other client’s publishing stream. To put it another way, if you publish a stream with the name ‘Test’, the subscriber will have to use the name ‘Test’ to connect to it.
var ns:NetStream = new NetStream(netConnection, farID); ns.play(name);
Note how we needed a farID for the subscriber, and not the publisher. We can create as many publishing streams as we like and all they will do is sit there and wait for a connection. Subscribers, on the other hand, need to know exactly which computer in the world they’re supposed to be subscribing to.
Step 4: Transferring Data
Once a publishing stream is set up it can be used to send data. The netstream Send method takes two arguments: a ‘handler’ name, and a variable length set of parameters. You can pass any object you like as one of these parameters, including basic types like String, int and Number. Complex objects are automatically ‘serialized’ – that is, they have all their properties recorded on the sending side and then re-created on the recieving side. Arrays and ByteArrays copy just fine too.
The handler name corresponds directly to the name of a function that will eventually be called on the receiving side. The variable parameter list corresponds directly to the arguments the receiving function will be called with. So if a call is made such as:
var i:int = 42;
netStream.send("Test", "Is there anybody there?", i);
The receiver must have a method with the same name and a corresponding signature:
public function Test(message:String, num:int):void
{
trace(message + num);
}
On what object should this receiving method be defined? Any object you like. The NetStream instance has a property called client which can accept any object you assign to it. That’s the object on which the Flash Player will look for a method of the corresponding sending name. If there’s no method with that name, or if the number of parameters is incorrect, or if any of the argument types cannot be converted to the parameter type, an AsyncErrorEvent will be fired for the sender.