For all programmers who are skilled in C-style languages, and beginners who fish for new experience with these!

Report article RSS Feed Networking using Stratus and NetStreams. AS3

This tutorial will show you how to connect, send and receive text/images over the internet. We will talk about what Stratus is and how to combine this with a NetStream. As an example in this turorial we will create a small chat client that will allow you to send messages and images. ActionScript 3

Posted by moci on Aug 17th, 2010
Intermediate Client Side Coding.

ActionScript 3 tutorial.

This tutorial will show you how to connect, send and receive text/images over the internet. We will talk about what Stratus is and how to combine this with a NetStream.
As an example in this turorial we will create a small chat client that will allow you to send messages and images.

Stratus is a service currently hosted by Adobe, it allows flash clients (web or AIR) to talk directly between themselves without the need of a server that has to relay the messages to anyone subscribed to this server.

How does it work? When each client connects to Stratus, yes each client has to first connect to Stratus, it gets an ID. This ID is sent back to the client, once you know the target's ID you can connect to it. Now you no longer need Stratus because your actually working in p2p between the clients. You can also create a group or channel, when using a group clients can connect/disconnect to this group, and no longer to a specific client.

Stratus uses UDP to retrieve and deliver the messages, and it currently has no specific port associated – which means that when you (or your network admin) blocks unknown UDP ports Stratus will fail to connect. The other thing is that Stratus is currently hosted on Adobe (it's still in beta) and it may become a paid service or companies have to run a dedicated Stratus service on their own servers.

Once we have connected our clients to each other we set up a send and receive NetStream, this so we can send data to each other.

The demo application that we are going to create in this tutorial will work with 2 clients. Because this is not about making a nice looking application we are going to be working with a very basic UI.

To start, create a new flex project.

Networking using Stratus and NetStreams

Select the type as a web application (this will allow you to easily start multiple clients so you can test it) and as the SDK I use 4.1, now we are presented with an empty project. Paste in the following MXML so we have some basic application to run.

xml code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/mx"
         minWidth="955" minHeight="600"
         applicationComplete="connectToStratus()">
 
  <fx:Script>
    <![CDATA[
     
    ]]>
  </fx:Script>
 
  <fx:Declarations>
    <!-- Place non-visual elements
    (e.g., services, value objects) here -->
  </fx:Declarations>
 
  <s:layout>
    <s:VerticalLayout/>
  </s:layout>
 
  <s:TextInput editable="false" text="{myID}"/>
  <s:TextInput id="farIDTextInput" enter="peerConnect()"/>
  <s:TextArea height="200" id="logTextArea"/>
  <s:TextInput id="messageTextInput" enter="{sendMessage()}"/>
  <s:Button label="Generate image" click="{sendImage()}"/>
  <mx:Image id="imgImage" width="200" height="200"/>
</s:Application>

You'll see a few problem indicators, that's only because we have not yet created the proper functions yet. By the end of this tutorial you'll have fixed them all, great!

Before we can actually begin writing the code you will need a developer key. This key can be obtained here: Labs.adobe.com
Just press the link where it says “Sign up for a Stratus beta Developer key”.

Networking using Stratus and NetStreams

Now that you've got your key, we're good to go.

First we will need to connect to the Stratus service. We do this by using the correct URL and your developer key. The combination of both will allow you to retrieve an ID. So lets create some variables!

actionscript code:
private const STRATUS_KEY:String = "YOUR-DEVELOPER-KEY";
private const STRATUS_SERVER:String = "rtmfp://stratus.adobe.com";
[Bindable] private var myID:String = "";

The third variable will be used to display the client's own ID. We need to do this because the other client will need to know to who it has to connect. The [Bindable] keyword is there so the UI is updated automatically.

Next is the function that will connect to Stratus. To connect to “something” we will need a new variable called a NetConnection. This variable will be used to connect and retrieve the ID information.

actionscript code:
private var netCon:NetConnection;

private function connectToStratus():void
{
  netCon = new NetConnection();
  netCon.addEventListener(NetStatusEvent.NET_STATUS,
    netStatusHandler);
  netCon.connect(STRATUS_SERVER + "/" + STRATUS_KEY);
}
 

We create a new NetConnection, add an event listener so it will call a function when the connection gets any sort of change in the status of the connection. So now we have connected to the Stratus and it wants to send information back, we will need to create a new function that will handle this event.

Once we have a valid connection we also need to create a new NetStream that will allow us to send messages, below the netCon variable add the following variable:

actionscript code:
private var sendStream:NetStream;
 

Here is the function that will handle the status changes of the connection.

actionscript code:
private function netStatusHandler(e:NetStatusEvent):void
{
  switch (e.info.code)
  {
    case "NetConnection.Connect.Success":
      myID = netCon.nearID;
     
      sendStream = new NetStream(netCon,
        NetStream.DIRECT_CONNECTIONS);
      sendStream.addEventListener(NetStatusEvent.NET_STATUS,
        sendStatusHandler);
      sendStream.publish("chat");
      break;
  }
}
 

The new status string can be found in the e.info.code variable, we use a switch because this is not the only status of a connection and we will use another by the end of this tutorial. The status you see now is the one fired when the connected has successfully connected.

We then update our ID variable so the user can read the clients ID, and we set up a new NetStream used when sending data. We also listen to this variable's status so we need another function. Then we start to publish on the “chat” channel. This last statement is key, else you will not be sending anything at all! The receiving end should listen to the “chat” channel to retrieve any data send.

When the status of the NetStream changes it means that someone has connected to the stream and wants to retrieve the data. We can use this event to automatically connect to that client so we have a bi-directional channel. For the receive stream we need a new variable, below the sendStream variable add the following:

actionscript code:
private var receiveStream:NetStream;
 

There, that's the last variable this example project needs. Now to write a few more functions.
The first one is the handler of the send stream's status.

actionscript code:
private function sendStatusHandler(e:NetStatusEvent):void
{
  if (e.info.code == "NetStream.Play.Start")
  {
    if (receiveStream == null)
    {
      farIDTextInput.text = e.target.peerStreams[0].farID;
      peerConnect();
    }
  }
}
 

We could have used a switch, to replace the first if statement, but because we do not care for other cases it doesn't really matter. The second if statement is to avoid a loop, because there is a chance that the start status will fire an event when you are already have both streams connected.
(For instance when a third client tries to connect)

We retrieve the other client's ID with e.target.peerStreams[0].farID, there might be a shorter version but this works. Next we auto-connect to this client by using the peerConnect() function, which is the next one we will write.

actionscript code:
private function peerConnect():void
{
  receiveStream = new NetStream(netCon, farIDTextInput.text);
  receiveStream.client = this;
  receiveStream.addEventListener(NetStatusEvent.NET_STATUS,
    receiveStatusHandler);
  receiveStream.play("chat");
}
 

This is basically the same as the send stream, the big difference here is that we connect to the ID of the other client. We want the stream to control the application so we set this as the client of the stream. You'll see what I mean when we start sending data. We add a handler for changes in the status and start listening to the “chat” channel.

Now that we've setup our receiveStream, we also want to clear the connection when the other client disconnects. To do this we update the switch from the net status handler to manually set the receiveStream to null.

actionscript code:
private function netStatusHandler(e:NetStatusEvent):void
{
  switch (e.info.code)
  {
    case "NetConnection.Connect.Success":
      myID = netCon.nearID;
     
      sendStream = new NetStream(netCon,
        NetStream.DIRECT_CONNECTIONS);
      sendStream.addEventListener(NetStatusEvent.NET_STATUS,
        sendStatusHandler);
      sendStream.publish("chat");
      break;
    case "NetStream.Connect.Closed":
      receiveStream = null;
      break;
  }
}
 

So, now you see why the switch came in handy in this function, the other handlers only respond to one status. There only needs one more function that handles the change in status for the receiveStream.

actionscript code:
private function receiveStatusHandler(e:NetStatusEvent):void
{
  if (e.info.code == "NetStream.Play.Start")
    trace("connection succes!");
}
 

This function actually doesn't do anything, but I've added it as a hint. In a real application you will want to disable all or portions of the UI until the application is fully initialized, including the connections for send and receive streams.

Great! We've got our connection initialization done! Now it's time to actually send some messages and images.

actionscript code:
private function sendMessage():void
{
  if (sendStream == null) return;
 
  logTextArea.appendText(messageTextInput.text + "\n");
  sendStream.send("messageReceived", messageTextInput.text);
  messageTextInput.text = "";
}

private function sendImage():void
{
  if (sendStream == null) return;
 
  var bd:BitmapData = new BitmapData(200, 200, false,
    Math.random() * 0x777777);
  sendStream.send("imageReceived",
    bd.getPixels(new Rectangle(0, 0, 200, 200)));
  imgImage.source = new Bitmap(bd);
}
 

Both send functions work in the same way, you get some kind of data and use the sendStream to send it. Remember what I said about the receiveStream “controlling” the application? Here is where you need this, you're basically calling a function on the other end and giving that function some parameters. In our case for each send function we also have a receive function that accepts either text or an object.

To make it easy for this tutorial we generate a new image, solid colored rectangle, and send that to the other client.

actionscript code:
public function messageReceived(e:String):void
{
  logTextArea.appendText("-- " + e + "\n");
}

public function imageReceived(e:Object):void
{
  var bd:BitmapData = new BitmapData(200, 200);
  bd.setPixels(new Rectangle(0, 0, 200, 200), e as ByteArray);
 
  var bmp:Bitmap = new Bitmap(bd);
  imgImage.source = bmp;
}
 

These last two functions get called when the receiveStream receives data, and what function is called depends on the sendStream that sends it.

To test this application you'll have to copy one ID and enter it in the empty text field under the other client it's ID text field. Press ENTER to connect, the other client should update the empty ID field automatically and connect it's stream automatically.

Send text messages by entering some text in the text field above the button and press ENTER.
Send images by pressing the 'generate image' button.

Networking using Stratus and NetStreams

Now you have an idea on how to setup your connection using Stratus and NetStreams.
Check out the full code below.

Take care,

Moci / Mathias

actionscript code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/mx"
         minWidth="955" minHeight="600"
         applicationComplete="connectToStratus()">
 
  <fx:Script>
    <![CDATA[
private const STRATUS_KEY:String = "YOUR-DEVELOPER-KEY";
private const STRATUS_SERVER:String = "rtmfp://stratus.adobe.com";
[Bindable] private var myID:String = "";

private var netCon:NetConnection;
private var sendStream:NetStream;
private var receiveStream:NetStream;

//Connect to stratus and start listening for status changes.
private function connectToStratus():void
{
  netCon = new NetConnection();
  netCon.addEventListener(NetStatusEvent.NET_STATUS,
    netStatusHandler);
  netCon.connect(STRATUS_SERVER + "/" + STRATUS_KEY);
}

//The status has been changed and we either create a
//sendStream or reset the receiveStream.
private function netStatusHandler(e:NetStatusEvent):void
{
  switch (e.info.code)
  {
    case "NetConnection.Connect.Success":
      myID = netCon.nearID;
     
      sendStream = new NetStream(netCon,
        NetStream.DIRECT_CONNECTIONS);
      sendStream.addEventListener(NetStatusEvent.NET_STATUS,
        sendStatusHandler);
      sendStream.publish("chat");
      break;
    case "NetStream.Connect.Closed":
      receiveStream = null;
      break;
  }
}

//The status of the sendStream has changed.
//We can now connect to the farID, which is the ID of the client
//that is connecting to our sendStream.
private function sendStatusHandler(e:NetStatusEvent):void
{
  if (e.info.code == "NetStream.Play.Start")
  {
    if (receiveStream == null)
    {
      farIDTextInput.text = e.target.peerStreams[0].farID;
      peerConnect();
    }
  }
}

//We create a receiveStream and connect it to the other client.
private function peerConnect():void
{
  receiveStream = new NetStream(netCon, farIDTextInput.text);
  receiveStream.client = this;
  receiveStream.addEventListener(NetStatusEvent.NET_STATUS,
    receiveStatusHandler);
  receiveStream.play("chat");
}

//The status of the receiveStream has changed.
private function receiveStatusHandler(e:NetStatusEvent):void
{
  if (e.info.code == "NetStream.Play.Start")
    trace("connection succes!");
}

//Send a text message, call the messageReceived function.
private function sendMessage():void
{
  if (sendStream == null) return;
 
  logTextArea.appendText(messageTextInput.text + "\n");
  sendStream.send("messageReceived", messageTextInput.text);
  messageTextInput.text = "";
}

//Send an image, call the imageReceived function.
private function sendImage():void
{
  if (sendStream == null) return;
 
  var bd:BitmapData = new BitmapData(200, 200, false,
    Math.random() * 0x777777);
  sendStream.send("imageReceived",
    bd.getPixels(new Rectangle(0, 0, 200, 200)));
  imgImage.source = new Bitmap(bd);
}

//We received some text, append it to our chat log.
public function messageReceived(e:String):void
{
  logTextArea.appendText("-- " + e + "\n");
}

//We received an image, make it visible in the UI.
public function imageReceived(e:Object):void
{
  var bd:BitmapData = new BitmapData(200, 200);
  bd.setPixels(new Rectangle(0, 0, 200, 200), e as ByteArray);
 
  var bmp:Bitmap = new Bitmap(bd);
  imgImage.source = bmp;
}
    ]]>
  </fx:Script>
 
  <fx:Declarations>
    <!-- Place non-visual elements
    (e.g., services, value objects) here -->
  </fx:Declarations>
 
  <s:layout>
    <s:VerticalLayout/>
  </s:layout>
 
  <s:TextInput editable="false" text="{myID}"/>
  <s:TextInput id="farIDTextInput" enter="peerConnect()"/>
  <s:TextArea height="200" id="logTextArea"/>
  <s:TextInput id="messageTextInput" enter="{sendMessage()}"/>
  <s:Button label="Generate image" click="{sendImage()}"/>
  <mx:Image id="imgImage" width="200" height="200"/>
</s:Application>

 
Post comment Comments
agudo64
agudo64 Nov 10 2010, 11:47am says:

Hello,
Thank you for this tutorial.
I am experiencing a problem I don't understand.
I have my Adobe Developer Key, I can connect to Stratus and I receive the netConnection.nearID. However, when I try to connect to the stream with crossed nearID and farID I don't receive any message at receiveStatusHandler such as "NetStream.Play.Start" or even an error event!
I am working with Flash AS3 and I am trying to develop a text/video chat module. I still haven't looked at the video part as the text one doesn't work... :o(
Thank you for your attention and potential help!!!

+2 votes     reply to comment
moci
moci Nov 22 2010, 5:37pm replied:

Hello, could you please send me your project (without your key of course, I'll add in my own). I'll take a look.

+1 vote     reply to comment
moci
moci Nov 22 2010, 5:44pm replied:

Also be sure that you have nothing "blocking" (firewalls or other security settings) the connection. When I was first using Stratus - inside a company network - I could retrieve my id but couldn't connect to another player.

+1 vote     reply to comment
Post a Comment
click to sign in

You are not logged in, your comment will be anonymous unless you join the community today (totally free - or sign in with your social account on the right) which we encourage all contributors to do.

2000 characters limit; HTML formatting and smileys are not supported - text only

Established
Jul 20, 2010
Privacy
Public
Subscription
Open to all members
Contact
Send Message
Email
Members Only
Membership
Join this group
Group Watch
Track this group
Tutorial
Browse
Tutorials
Report Abuse
Report article
Related Groups
Curly Bracket Programming Realm
Curly Bracket Programming Realm Hobbies & Interests group with 74 members
Indie Devs
Indie Devs Hobbies & Interests group with 1,104 members
IntoGames
IntoGames Developer & Publisher