Posts Tagged ‘Red5’

Red5 provides us with the abstraction of a hierarchy of scopes or “rooms”, to which clients can connect. In their most basic usage, scopes are simply used as rooms where for example shared objects used for chat applications or live media streams are located. However in the development of advanced MMOs and other applications, we want to push our usage of scopes a little further. This post aims to share some tips that can help those wishing to use Red5 scopes.

First a quick recap of the scope system. Red5 assigns each application a default scope, known as the “WebScope”. This scope exists whether or not anyone is connected to it (more on scope lifetime later). Once clients connect to scopes, or we otherwise create them, you get a tree structure.

You will likely have created your Red5 application by extending the ApplicationAdapter class. When you override the connect() callback method of this class, this will be called when clients connect. It is important to realize that it will be called for the entire scope hierarchy that the client is connecting to. So the following code:

In the above output, first the client is connected to /default/myApp your WebScope, then down through successive parent scopes to the target leaf scope. At any stage, you can prevent connection by calling rejectClient(<REASON>), or throwing a ClientRejectedException(<REASON>) deep in your stack. The client will receive the reason for the rejection in their error handler e.g.

Those new to Red5, or those not having to build scalable applications, may be tempted to stop reading now. They might now think they know all they need to know to implement an authentication scheme e.g.

Unfortunately there is a little more to it than that. First of all, you need to notice the innocuous synchronize statement inserted before the ApplicationAdapter.connect method.

If you think about it, this little statement will have the effect of serializing execution of the connect() callback, and thus serializing the process of connecting to your application. Therefore if, as is most likely, your authentication code involves a database lookup to retrieve the password for the user, if there are multiple clients trying to connect at once many will spend ages in a “connection” queue – database lookups take *time* so you don’t want to serialize operations!

The next thing you should therefore do, is remove the synchronized statements from your callbacks by deriving your application from MultiThreadedApplicationAdapter rather than ApplicationAdapter as is typical i.e.

If for the sake of experimentation (1) change your connect() implementation to the following code (2) open a couple of Flex clients in your browser (3) make them connect simultaneously (4) have a look at the output in your Eclipse Console window… you will notice that the calls to connect() are *still* serialized.

The problem is that somewhere deep inside the Red5 architecture is a global lock which is serializing calls to connect() for you. The only way around this performance problem is to use lazy authentication, as described in the next section.

3. Lazy authentication on demand

Now it maybe that you have a simple application, where the user authenticates themselves when they connect, and thereafter connects to scopes and performs actions as an authenticated user. Fine.

But what if you want to make things a little more complicated, by having for example two scopes, one of which contains publicly available functionality, and the other which contains functionality only authenticated users can access e.g.
WebScope
WebScope->signup
WebScope->game
Note: I apologise if these examples get rather contrived, but they will help illustrate some points!

The solution is to add an authentication method to your WebScope. This is really easy to achieve, simply add a *public* method to the application class you derived from MultiThreadedApplicationAdapter e.g.

As you will have no doubt realized, there is a problem with this approach. In order to call authenticate(), your client needs to be connected to the default root scope (the WebScope). Furthermore, you need a way of preventing your client from connecting to protected scopes without the necessary privileges.

You might hope the solution is to (1) mark clients as authenticated using attributes, and (2) provide another method that effects dynamic server-side navigation to your scopes. So we now have inside our application class:

This method kind of works, but there are a bunch of gotchas related to dynamically changing scope, which I shall iterate to finish of this post.

1. You change a client’s scope, by joining it connection to that destination scope.
2. When you join the connection, this will cause Red5 to create a completely new Client object, even though the actual (remote) client is unchanged

The problem is that deep inside Red5 your call to conn.connect above will result in your overridden version of the connect application callback being called. Unfortunately, it will be called with a new client object that does not have the Authenticated attribute you set, and which will therefore cause your code to call rejectClient.

6. Forget authenticating in connect and all is sweet…

The solution is to take your overridden application connect callback method out of the authentication process completely. You should now check whether someone is allowed to connect to a scope inside setScope.

If you’ve read this article and like the idea of setting up your own Scope structure and advanced authentication scheme, read on for just a couple of moments. These two tips could save you some more time!

Firstly, you may have used your application startup callback to initialize your scope structure. For example:

You find that the first time you connect a client to your scope, everything works fine, but the second time you connect your client to your scope, you get an error saying the scope doesn’t exist.

The reason is that by default, Red5 tears down scopes when the last client disconnects from them. If you wish to setup standard scopes, and have them permanently in memory, for example preserving state irrespective of whether clients are connected, you need to pin them there.

The simplest way to pin your standard scope is to make your application “listen” to events on them. For example:

Finally, you might have realized from the above that once a client has connected to a scope, they will no longer have access to the setScope method we defined and will therefore be unable to dynamically change scope again!!

The solution here is to package your setScope function into a special handler that you will add to every scope. For example:

The next step is to install the demos you want to try using a flash control panel that comes packaged with the distribution. Now, be warned, as of version 0.9.0 RC2, the distribution is a little flaky. So when you click the link to “install” demos on the page you just loaded, it won’t work.