预备知识

为了充分利用本教程,你需要具有使用 Adobe Flash Builder创建应用程序的先前经验以及关于使用 .NET和 Microsoft Visual Studio开发技巧方面的一些知识。

关于远端共享对象

RSOs 能够在多个客户端应用程序之间跟综、存储、共享和同步数据。

RSO是驻留于服务器的对象。 它驻留于一个连接客户端的信息应用程序中。 多个客户端能够连接到一个RSO并且它们均能够在RSO中访问相关数据。 在本例中,WebORB负责管理RSO 并且为各种客户端提供访问RSO的权利。

当同时用于多个客户端时RSOs特别有用。 当一个客户端更改数据导致在服务器中更新RSO,则服务器将相应的更改信息发送到所有其它相连的客户端,这样,你可以利用相同数据对许多不同服务器进行同 步。 此外,服务器也能够更新和访问RSOs,这为开发人员的应用程序开发提供了更多选项。

概括来说,RSOs能够用于:

  • 自动同步任何类型的数据,例如,用于协作web应用程序。

  • 发送消息至所有连接到相同空间的客户端,例如,用于聊天应用程序。

在本教程中,你将使用RSOs创建一个简单在线版本的添加单词(Add-a-word)游戏。 该游戏的目的是在一个语句中添加一个单词,一个用户一次,最后组成一个很长的语句(但仍然具有意义)。

请查看下面范例:

  • 两个联网用户开始进行添加单词游戏。

  • 应用程序为每个联网用户指派次序。

  • 用户1开始进行游戏,输入"My" 并且点击 Submit。 一旦用户1提交一个单词,则下一步轮到另一个用户。 此时,应用程序将自动禁止第一个用户使用submit按钮,并且打开另一个用户的submit按钮。

  • 用户2输入 "house" 并且点击Submit按钮,同时将控制权返回给用户 1。

  • 用户1输入 "is",如此反复。

  • 在游戏过程中,RSO 能够跟综语句的内容。 到此为止,它将读取"My house is"。

编写服务器侧代码

该应用程序的服务器侧代码能够跟综所有联网用户,指派次序,并且在语句中添加单词。

注意: 下面步骤包含的代码片段不是完整的程序,它们仅仅用于说明服务器侧实现的主要概念。 关于相关的完整代码,参见本教程范例文件中的WeborbSharpRSO.cs。

按照下列步骤创建相关的服务器侧DLL:

  1. 在Microsoft Visual Studio中创建一个Class Library项目。

  2. 在该项目中,将一个引用添加至Weborb.dll。该库位于你的WebORB安装文件夹的 bin 文件夹中。

  3. 创建main 类,在本例中,它的名称为WeborbSharpRSO。 该类可以扩展 Weborb.Messaging.Server.Adapter.ApplicationAdapter,它支持你创建 WebORB 应用程序:

    namespace WeborbSharpRSO    {    public class WeborbSharpRSO : ApplicationAdapter    {    }    }

    ApplicationAdapter 类具有若干方法,它们能够让你知道应用程序什么时候启动,新的空间什么时候创建,以及客户端什么时候连接或中断。 一个应用程序可以具有多个同时运行的空间。 每个空间可以具有一个或多个连接的客户端和共享信息。 连接到一个空间的客户端仅仅能够与相同空间的其它客户端共享信息。

    在本例中,你将使用下列两个方法来检测客户端什么时候加入或离开一个空间:

    public override bool roomJoin(IClient client, IScope room)    public override void roomLeave(IClient client, IScope room)    
  4. 你可以在类的上面声明Remote Shared Object名称:

    public string sharedObjectName = "addWord";
  5.  roomJoin() 方法中,首先检查该用户是否连接到相关的空间。 然后,检查该空间是否具有Remote Shared Object。 如果没有,创建一个Remote Shared Object并且将一个SharedObjectListener添加到其中。 该侦听器将检测RSO的任何变化。

    public override bool roomJoin(IClient client, IScope room){    if (base.roomJoin(client, room)) {    ISharedObject so;    if (!hasSharedObject(room, sharedObjectName)){    createSharedObject(room, sharedObjectName, false);    so = getSharedObject(room, sharedObjectName);    so.addSharedObjectListener(new MySharedObjectListener());    }    else    so = getSharedObject(room, sharedObjectName);    …    }    }

    注意: roomJoin的代码也能够放置于appStart方法的内部。

    下一步是检查和更新RSO的一些属性的值,例如连接用户的数量以及轮换次序等。 这些属性包含所有你希望在客户端中共享的信息。

    例如,你的代码能够从共享对象(shared object (SO))获得连接到相关空间的现有用户的数量 ,然后将该数量加1。 之后,将该值写回到SO中:

    if(so.hasAttribute("totalUsers"))    totalUsers = so.getLongAttribute("totalUsers");    totalUsers++;    …    so.setAttribute("totalUsers", totalUsers);    

    除了totalUsers之外,RSO 还能够共享下列数据:

    • userList:连接到相同空间的所有客户端的列表

    • currentUser:轮到添加下一个单词的客户端的ID和名称

    • sentence:实际形成的语句

    • word:上次提交的单词

      可以根据连接次序为连接到空间的每个客户端指派一个唯一ID。

  6. 为了构成完整的roomJoin() 函数,你必须添加下列代码,它能够使用invokeClients方法将该ID发送到客户端 (本文将在后面给出更为详细解释):

    object[] args = new Object[] {client.getId()};    invokeClients("SetUserId", args, client.getConnections(room));    
  7. 下一步,添加 Listener类以便实现Weborb.Messaging.Api.SO.ISharedObjectListener接口:

    public class MySharedObjectListener : ISharedObjectListener    {    }    

    该类提供能够用于在共享对象中检查不同类型变化的方法。 本教程主要讨论 onSharedObjectUpdate() 方法,该方法用于检查远端对象的变化。 RSO的word 属性变化将会调用一个可以处理相关信息和设置下一个用户的方法。

  8. 按照下列形式更新onSharedObjectUpdate():

    public void onSharedObjectUpdate(ISharedObjectBase so, string key, object value){    if (key == "word")    WeborbSharpRSO.nextUser(so, value.ToString());    }    

    当在语句中添加一个新的单词时,将调用nextUser()方法。 即使你能够在该函数中直接修改 RSO,你也不要使用这一方法

    在客户端侧发出请求过程中,当服务器侧代码更改RSO时,发出请求的客户端不能获得由服务器添加的更改内容。 服务器侧将所有的更改内容作为独立事件进行累积。 这些累积的事件首先发送到始发者,然后再发送到所有其它用户。 每个事件均有一个相应的RSO版本号。 如果存在多个具有相同版本号的更改事件需要发出,Flash Player只会允许第一个事件通过。 这将导致发起请求的客户端只能获得它请求的更改内容,而不能获得服务器根据相同请求添加到RSO的其它更改。

  9. 为了满足这一要求,在实际服务器侧RSO更改将发生的位置启动一个新的线程:

    public static void nextUser(ISharedObjectBase so,string word) {    ThreadPool.QueueUserWorkItem(ChangeCurrentUser, new object[] {so, word});    }    
  10. 添加一个ChangeCurrentUser()函数以便在服务器中实际更改 RSO。 下面是你将单词添加到语句中以及将其写回到RSO中的位置:

    public static void changeCurrentUser( object state ){    …    if( word != "" ){    string text = "";    if( so.hasAttribute("sentence") )    text = so.getStringAttribute("sentence");    so.setAttribute("sentence", text += " " + word);    }    …    }    
  11. 使用这一相同函数确定谁是下一个用户,然后也将该信息写回到RSO中。

    so.setAttribute("currentUser", newCurrentUser.ToString() );
  12. 添加roomLeave()方法以便在用户离开空间时处理清除任务。 该方法能够更新仍在进行游戏的客户端的列表。

    注意下面的三步骤过程:首先检索userList属性的内容并且将其设置为users字典,然后从字典中删除将要离开空间的客户端,最后将该对象的一份内容的放入一个newUsers字典。 造成这一棘手过程的原因是当删除一个元素时服务器不能检测到原始users对象的更改。 因此,如果你将相同的对象送回RSO,则相应的更改不能发送到相关的客户端。

    users = so.getMapAttribute("userList");    …    if (users.Contains(client.getId()))    users.Remove(client.getId());    newUsers = new Dictionary<string, string>();    foreach (DictionaryEntry de in users)    newUsers[de.Key] = de.Value;    …    so.setAttribute("totalUsers", totalUsers);    
  13. 最后,将invokeClients()添加到 main类。 该函数在客户端侧使用connection.invoke 方法调用 ActionScript 方法。 你需要传递调用的方法的名称,任何需要的参数以及客户端连接的列表。 在functionName变量中使用的名称必须以客户端应用程序的一个函数名称的形式存在。

    private void invokeClients(string functionName, object[] args, IList<IConnection> ILconn ){    foreach(IConnection conn in ILconn){    ((IServiceCapableConnection)conn).invoke(functionName, args);    }    }    

    关于 invoke() 方法的详细说明已经超出本教程的讨论范围。 关于该方法的更多信息,请查阅 WebORB 文档或参见 从.NET调用ActionScript 函数

  14. 一旦你完成该类的代码编写,创建相应的DLL并且将其复制到WebORB文件夹的bin录下。


 

本产品经 Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License 许可。Adobe 提供超出该许可范围、与本产品包含的代码示例相关的权限。

关于作者

Damian Piccolo是一名信息系统工程师,并且是Anden Solutions的主席。他拥有超过8年的Flash、Flex、PHP和.NET应用程序的设计和开发经验。他使用WebORB超过3年时间。

Esteban Yofre 是Anden Solutions的首席开发者。他的Flash和Flex开发经验丰富,包括在过去3年中使用WebORB构建解决方案。