以下是 FCS/FMS 的 application 类和 Red5 的 ApplicationAdapter http://dl.fancycode.com/red5/api/org/red5/server/adapter/ApplicationAdapter.html 类的方法的一个简单对比。
FCS / FMS | Red5 |
onAppStart | appStart \\ roomStart |
onAppStop | appStop \\ roomStop |
onConnect | appConnect \\ roomConnect \\ appJoin \\ roomJoin |
onDisconnect | appDisconnect \\ roomDisconnect \\ appLeave \\ roomLeave |
Red5 支持两种不同的方式来从一个被调用的方法中访问当前连接。连接可以用于获取活动客户端以及他连接到的域。第一种可能性(方式)就是使用"神奇的"Red5http://dl.fancycode.com/red5/api/org/red5/server/api/Red5.html 对象:
import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.IScope; import org.red5.server.api.Red5; public void whoami() { IConnection conn = Red5.getConnectionLocal(); IClient client = conn.getClient(); IScope scope = conn.getScope(); // ... }
第二种可能性(方式)需要这个方法定义一个 IConnection http://dl.fancycode.com/red5/api/org/red5/server/api/IConnection.html 类型的参数作为隐式的第一个参数,当一个客户端调用此方法时这个参数会被 Red5 自动添加:
import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.IScope; public void whoami(IConnection conn) { IClient client = conn.getClient(); IScope scope = conn.getScope(); // ... }
这些处理程序可以被客户端直接调用:
nc = new NetConnection(); nc.connect("rtmp://localhost/myapp"); nc.call("handler.method", nc, "Hello world!");
举例,将类 com.fancycode.red5.HandlerSample 作为一个处理程序进行注册,以下 bean 需要添加到 WEB-INF/red5-web.xml:
<bean id="sample.service" class="com.fancycode.red5.HandlerSample" singleton="true" />
仍然拿上边 com.fancycode.red5.HandlerSample 类作为例子,在代码中手工注册如下所示:
public boolean appStart(IScope app) { if (!super.appStart(scope)) return false; Object handler = new com.fancycode.red5.HandlerSample(); app.registerServiceHandler("sample", handler); return true; }
Red5 应用程序要想调用客户端方法的话,首先得需要一个对当前连接对象的引用:
import org.red5.server.api.IConnection; import org.red5.server.api.Red5; import org.red5.server.api.service.IServiceCapableConnection; ... IConnection conn = Red5.getConnectionLocal();
如果连接实现了 IServiceCapableConnection http://dl.fancycode.com/red5/api/org/red5/server/api/service/IServiceCapableConnection.html 接口的话,它将支持调用另一端的方法:
if (conn instanceof IServiceCapableConnection) { IServiceCapableConnection sc = (IServiceCapableConnection) conn; sc.invoke("the_method", new Object[]{"One", 1}); }
如果你需要拿到方法执行的结果,那你需要提供一个实现了 IPendingServiceCallback http://dl.fancycode.com/red5/api/org/red5/server/api/service/IPendingServiceCallback.html 接口的类:
import org.red5.server.api.service.IPendingService; import org.red5.server.api.service.IPendingServiceCallback; class MyCallback implements IPendingServiceCallback { public void resultReceived(IPendingServiceCall call) { // Do something with "call.getResult()" } }
调用方法的代码如下:
if (conn instanceof IServiceCapableConnection) { IServiceCapableConnection sc = (IServiceCapableConnection) conn; sc.invoke("the_method", new Object[]{"One", 1}, new MyCallback()); }
当一个 room 创建时要创建一个新的共享对象的话,你可以在应用中重写 roomStart 方法:
import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.IScope; import org.red5.server.api.so.ISharedObject; public class SampleApplication extends ApplicationAdapter { public boolean roomStart(IScope room) { if (!super.roomStart(room)) return false; createSharedObject(room, "sampleSO", true); ISharedObject so = getSharedObject(room, "sampleSO"); // Now you could do something with the shared object... return true; } }
如果想像在 FCS / FM 中的 onSync 方法那样得到在共享对象变化时的通知,监听器就应该实现 ISharedObjectListenerhttp://dl.fancycode.com/red5/api/org/red5/server/api/so/ISharedObjectListener.html 接口:
import org.red5.server.api.so.ISharedObject; import org.red5.server.api.so.ISharedObjectListener; public class SampleSharedObjectListener Migration Guide implements ISharedObjectListener { public void onSharedObjectUpdate(ISharedObject so, String key, Object value) { // The attribute <key> of the shared object <so> // was changed to <value>. } public void onSharedObjectDelete(ISharedObject so, String key) { // The attribute <key> of the shared object <so> was deleted. } public void onSharedObjectSend(ISharedObject so, String method, List params) { // The handler <method> of the shared object <so> was called // with the parameters <params>. } // Other methods as described in the interface... }
另外,这个监听器还必须得在共享对象中注册一下:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.addSharedObjectListener(new SampleSharedObjectListener())
一个共享对象也可以由服务这样进行更改:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.setAttribute("fullname", "Sample user");
如果想把共享对象上的多个动作并入一个对于相关客户端改变事件的话,必须得用 beginUpdate 和 endUpdate 方法:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.beginUpdate(); so.setAttribute("One", "1"); so.setAttribute("Two", "2"); so.removeAttribute("Three"); so.endUpdate();
Flash 客户端通过 remote_so.send(<handler>, <args>) 对共享对象的处理程序的调用或者相应服务端调用可以在 Red5 映射到方法。因此处理程序必须通过 ISharedObjectHandlerProviderhttp://dl.fancycode.com/red5/api/org/red5/server/api/so/ISharedObjectHandlerProvider.html 接口的方法注册:
package com.fancycode.red5; class MySharedObjectHandler { public void myMethod(String arg1) { // Now do something } } ... ISharedObject so = getSharedObject(scope, "sampleSO"); so.registerServiceHandler(new MySharedObjectHandler());
处理程序也可以在注册时命名:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.registerServiceHandler("one.two", new MySharedObjectHandler());
这里,方法可以通过 one.two.myMethod 被调用。另一个定义共享对象的事件处理程序就是将其添加进 red5-web.xml,类似于基于文件的应用处理程序。bean 必须带有 <SharedObjectName>.<DottedServiceName>.soservice 名字,因此以上例子也可以这样定义:
<bean id="sampleSO.one.two.soservice" class="com.fancycode.red5.MySharedObjectHandler" singleton="true" />
以下是一个序列化和反序列化的例子:
import java.io.IOException; import org.red5.io.object.Input; import org.red5.io.object.Output; import org.red5.server.api.persistence.IPersistable; class MyPersistentObject implements IPersistable { // Attribute that will be made persistent private String data = "My persistent value"; void serialize(Output output) throws IOException { // Save the objects's data. output.writeString(data); } void deserialize(Input input) throws IOException { // Load the object's data. data = input.readString(); } // Other methods as described in the interface... }
要保存或者加载这样一个对象,可以使用如下代码:
import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.IScope; import org.red5.server.api.Red5; import org.red5.server.api.persistence.IPersistenceStore; class MyApplication extends ApplicationAdapter { private void saveObject(MyPersistentObject object) { // Get current scope. IScope scope = Red5.getConnectionLocal().getScope(); // Save object in current scope. scope.getStore().save(object); } private void loadObject(MyPersistentObject object) { // Get current scope. IScope scope = Red5.getConnectionLocal().getScope(); // Load object from current scope. scope.getStore().load(object); } }
可以像这样进行对象的注册:
import org.red5.server.api.IScope; import org.red5.server.api.IScheduledJob; import org.red5.server.api.ISchedulingService; import org.red5.server.adapter.ApplicationAdapter; class MyJob implements IScheduledJob { public void execute(ISchedulingService service) { // Do something } } public class SampleApplication extends ApplicationAdapter { public boolean roomStart(IScope room) { if (!super.roomStart(room)) return false; // Schedule invokation of job every 10 seconds. String id = addScheduledJob(10000, new MyJob()); room.setAttribute("MyJobId", id); return true; } }
这个由 addScheduledJob 返回的 id 以后可以用来停止注册进来的工作的执行:
public void roomStop(IScope room) { String id = (String) room.getAttribute("MyJobId"); removeScheduledJob(id); super.roomStop(room); }
为了支持远程调用,在 WEB-INF/web.xml 文件里需要添加以下部分:
<servlet> <servlet-name>gateway</servlet-name> <servlet-class> org.red5.server.net.servlet.AMFGatewayServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>gateway</servlet-name> <url-pattern>/gateway/*</url-pattern> </servlet-mapping>
在 <url-pattern> 标签中指定的路径(这里是 gateway)就可以作为连接 url 被远程客户端调用。假如应用 myApp 已经制定了本示例,那么这个 URL 可以是:
http://localhost:5080/myApp/gateway
通过被连接被调用的方法将会在当前应用域的上下文中执行。如果想要这些方法在子域执行的话,URL 中就应该加上相应的子域:
http://localhost:5080/myApp/gateway/room1/room2
以下服务端代码示例解释了如何使用远程客户端:
import org.red5.server.net.remoting.RemotingClient; String url = "http://server/path/to/service"; RemotingClient client = new RemotingClient(url); Object[] args = new Object[]{"Hello world!"}; Object result = client.invokeMethod("service.remotingMethod", args); // Now do something with the result
被调用服务器上的一些方法可能需要相当长的时间才能执行完,因此为了避免线程阻塞 Red5 服务最好是异步执行调用。因此应该有一个实现了接口 IRemotingCallbackhttp://dl.fancycode.com/red5/api/org/red5/server/net/remoting/IRemotingCallback.html 的对象作为附加参数:
import org.red5.server.net.remoting.RemotingClient; import org.red5.server.net.remoting.IRemotingCallback; public class CallbackHandler implements IRemotingCallback { void errorReceived(RemotingClient client, String method, Object[] params, Throwable error) { // An error occurred while performing the remoting call. } void resultReceived(RemotingClient client, String method, Object[] params, Object result) { // The result was received from the server. } } String url = "http://server/path/to/service"; RemotingClient client = new RemotingClient(url); Object[] args = new Object[]{"Hello world!"}; IRemotingCallback callback = new CallbackHandler(); client.invokeMethod("service.remotingMethod", args, callback);