FluorineFx所提供的远程共享对象(Remote Shared Objects)和FMS的共享对象的功能是一样,对于熟悉FMS开发的朋友来说,学习FluorineFx的远程共享对象是非常简单的。
共享对象可以在服务器端创建,也可以在客户端创建。在客户端创建共享对象的方法和使用FMS开发是一样的,创建一个 NetConnection对象,通过该对象的connect()方法连接到服务器,然后通过SharedObject.getRemote()方法就可以在客户端创建一个远程共享对象。如下实例代码:
private function connectionServer(): void { var nc:NetConnection = new NetConnection(); nc.connect( " rtmp://localhost:1617/SOAPP " , " username " , " password " ) nc.addEventListener(NetStatusEvent.NET_STATUS,onStatusHandler); nc.client = this ; } private function onStatusHandler( event :NetStatusEvent): void { if ( event .info.code == " NetConnectin.Connect.Success " ) { createSharedObject(); } } private function createSharedObject(): void { var so:SharedObject = SharedObject.getRemote( " OnLineUsers " ,nc.uri, false ); so.addEventListener(SyncEvent.SYNC,onSyncHandler); so.connect( this .nc); so.client = this ; } private function onSyncHandler( event :SyncEvent): void { // ..do other}
在FluorineFx的服务器端创建远程共享对象和FMS有很大的区别,FluorineFx的 ISharedObjectService接口提供了专门用于创建远程共享对象的方法 CreateSharedObject(),ApplicationAdapter实现了此接口方法。定义如下:
public bool CreateSharedObject(IScope scope, string name, bool persistent) { ISharedObjectService service = (ISharedObjectService)ScopeUtils.GetScopeService(scope, typeof (ISharedObjectService)); return service.CreateSharedObject(scope, name, persistent); }
如果要在服务器端创建远程共享对象,直接调用ApplicationAdapter类中的CreateSharedObject()方法就可以。如下在FluorineFx服务器端创建远程共享对象的代码块:
ISharedObject users_so = GetSharedObject(connection.Scope, " OnLineUsers " ); if (users_so == null ) { // 创建共享对象 CreateSharedObject(connection.Scope, " OnLineUsers " , false ); users_so = GetSharedObject(connection.Scope, " OnLineUsers " ); }
要想更新共享对象里的数据客户端还是使用setProperty()方法,而FluorineFx的服务器更新共享对象的方法则与 FMS不一样,使用的是FluorineFx.Messaging.Api.IAttributeStore接口提供的SetAttribute()和 RemoveAttribute()方法来更新共享对象里的数据。
陆续介绍了这么多,下面通过一个案例来看看该这么去应用远程共享对象。比如做IM、视频聊天、视频会议等及时通信类型的应用中,用户上线下线的频率非常高,这时候我们就可以使用远程共享对象去做在线用户的数据同步。
首先建立FluorineFx服务库,并建立一个应用类继承于ApplicationAdapter,通过重写ApplicationAdapter的相关方法来实现应用程序的不同需求,详细如下代码块:
using System; using System.Collections.Generic; using System.Text; using FluorineFx.Messaging.Adapter; using FluorineFx; using FluorineFx.Messaging.Api; using System.Diagnostics; using FluorineFx.Messaging.Api.SO; using FluorineFx.Exceptions; using FluorineFx.Context; using FluorineFx.Messaging.Api.Service; using System.Collections; using Fx.Adapter.DTO; namespace Fx.Adapter { /// <summary> /// 自定义ApplicationAdapter /// </summary> [RemotingService] public class MyApp : ApplicationAdapter { /// <summary> /// 应用程序启动 /// </summary> /// <param name="application"></param> /// <returns></returns> public override bool AppStart(IScope application) { Trace.WriteLine( " 应用程序启动 " ); return true ; } /// <summary> /// 房间启动 /// </summary> /// <param name="room"></param> /// <returns></returns> public override bool RoomStart(IScope room) { Trace.WriteLine( " 房间启动 " ); if ( ! base .RoomStart(room)) return false ; return true ; } /// <summary> /// 接收客户端的连接 /// </summary> /// <param name="connection"></param> /// <param name="parameters"></param> /// <returns></returns> public override bool AppConnect(IConnection connection, object [] parameters) { string userName = parameters[ 0 ] as string ; string password = parameters[ 1 ] as string ; if (password == null || password == string .Empty) throw new ClientRejectedException( null ); connection.Client.SetAttribute( " userName " , userName); // 获取共享对象(OnLineUsers) ISharedObject users_so = GetSharedObject(connection.Scope, " OnLineUsers " ); if (users_so == null ) { // 创建共享对象 CreateSharedObject(connection.Scope, " OnLineUsers " , false ); users_so = GetSharedObject(connection.Scope, " OnLineUsers " ); } // 更新共享对象 users_so.SetAttribute(userName, userName); return true ; } /// <summary> /// 加入房间 /// </summary> /// <param name="client"></param> /// <param name="room"></param> /// <returns></returns> public override bool RoomJoin(IClient client, IScope room) { Trace.WriteLine( " 加入房间 " + room.Name); return true ; } /// <summary> /// 离开房间 /// </summary> /// <param name="client"></param> /// <param name="room"></param> public override void RoomLeave(IClient client, IScope room) { Trace.WriteLine( " 离开房间 " + room.Name); base .RoomLeave(client, room); } /// <summary> /// 用户退出 /// </summary> /// <param name="connection"></param> public override void AppDisconnect(IConnection connection) { string userName = connection.Client.GetAttribute( " userName " ) as string ; ISharedObject users_so = GetSharedObject(connection.Scope, " OnLineUsers " ); if (users_so != null ) { // 从共享对象中移除当前退出系统用户 users_so.RemoveAttribute(userName); } base .AppDisconnect(connection); } } }
开发好了ApplicationAdapter,还需要对此ApplicationAdapter进行通信配置,在FluorineFx的应用程序目录中添加app.config并进行如下配置:
<? xml version="1.0" encoding="utf-8" ?> < configuration > < application-handler type ="Fx.Adapter.MyApp" /> </ configuration >
另外还需要配置一个客户端方法的通信通道,通过FluorineFx网站下的WEB-INF/flex/service-config.xml配置:
<?xml version="1.0" encoding="utf-8" ?> <services-config>
<channels>
< channel-definition id ="my-rtmp" class ="mx.messaging.channels.RTMPChannel" > < endpoint uri ="rtmp://{server.name}:1617" class ="flex.messaging.endpoints.RTMPEndpoint" /> </ channel-definition >
</channels>
</services-config>
如上便完成了服务器端的开发,在flash/felx客户端通过NetConnection去连接应用,并根据当前的连接去连接服务器端的远程共享对象,最后通过异步事件来实现数据同步更新。如下程序运行截图:
此时开多个浏览器窗口测试,不同窗口使用不同的用户名登录,可以很清楚的看到,我们已经实现了在线用户的数据同步功能,可以及时的反映用户上线离线,可以及时的同步在线用户列表的数据。
另外远程共享对象还有一个功能非常强大的特性方法,就是连接到共享对象的客户端之间可以直接广播消息(客户端调用客户端的方法)。就以上面在线用户的案例为例,用户成功登陆服务器我需要广播一条消息,用户退出了我也需要广播一条消息,要实现这个功能就需要通过远程共享的客户端呼叫 (send()方法)来实现,如下代码块:
private function onCallClient(message:String): void { so.send(" onSayMessage " ,message); }
远程共享对象的send()方法调用了onSayMessage这个客户端方法来实现对连接到共享对象上的所有客户端广播消息,那么我们的在定义一个onSayMessage方法,如下:
/* * * 接受客户端呼叫---此方法必须是public修饰 */ public function onSayMessage(message:Object): void { traceWriteln(message.toString()); }
private function traceWriteln(param:String):void { txtTraceArea.htmlText += param + "\n"; txtTraceArea.validateNow(); txtTraceArea.verticalScrollPosition = txtTraceArea.maxVerticalScrollPosition; }
如果想实现用户退出广播,可以通过服务器端RPC的方法调用客户端的方法来实现,关于RPC请查看《Flex与.NET互操作(十一):基于FluorineFx.Net的及时通信应用(Remote Procedure Call)(二) 》有详细介绍。下面是Flex客户端的完整代码:
<? xml version = " 1.0 " encoding = " utf-8 " ?> < mx:Application xmlns:mx = " http://www.adobe.com/2006/mxml " layout = " absolute " width = " 530 " height = " 378 " backgroundGradientAlphas = " [1.0, 1.0] " backgroundGradientColors = " [#000000, #686868] " fontSize = " 12 " > < mx:Script > <! [CDATA[ import mx.controls.Alert; import dotnet.fluorinefx.VO.UserInfo; private var nc:NetConnection; private var so:SharedObject; private var info:UserInfo; private function connectionServer( event :MouseEvent): void { info = new UserInfo(); info.UserName = this .txtUserName.text; info.Password = this .txtPassword.text; nc = new NetConnection(); nc.connect( " rtmp://localhost:1617/SOAPP " ,info.UserName,info.Password); nc.addEventListener(NetStatusEvent.NET_STATUS,onStatusHandler); nc.client = this ; this .txtUserName.text = "" ; this .txtPassword.text = "" ; this .txtUserName.setFocus(); } private function onStatusHandler( event :NetStatusEvent): void { this .connStatus.text = " 连接状态: " + event .info.code; if ( event .info.code == " NetConnection.Connect.Success " ) { // 连接远程共享对象 so = SharedObject.getRemote( " OnLineUsers " ,nc.uri, false ); if (so) { so.addEventListener(SyncEvent.SYNC,onSyncHandler); so.connect(nc); so.client = this ; } onCallClient( " 用户【 <font color=\ " #4100b9\ " > " + info.UserName + " </font>】登陆了系统! " ); " ); } } private function onSyncHandler( event :SyncEvent): void { var temp:Array = new Array(); for (var u:String in so.data) { // traceWriteln("异步事件->共享对象:" + u + ":" + so.data[u]); temp.push(so.data[u]); } this .userList.dataProvider = temp; } private function traceWriteln(param:String): void { txtTraceArea.htmlText += param + " \n " ; txtTraceArea.validateNow(); txtTraceArea.verticalScrollPosition = txtTraceArea.maxVerticalScrollPosition; } private function onCallClient(message:String): void { so.send(" onSayMessage " ,message); } /* * * 接受客户端呼叫 */ public function onSayMessage(message:Object): void { traceWriteln(message.toString()); } ]]> </ mx:Script > < mx:Label x = " 24 " y = " 134 " id = " connStatus " width = " 288 " color = " #FFFFFF " /> < mx:List x = " 342 " y = " 10 " height = " 347 " width = " 160 " id = " userList " > </ mx:List > < mx:Form x = " 24 " y = " 10 " width = " 236 " > < mx:FormItem label = " 用户名: " color = " #FFFFFF " > < mx:TextInput id = " txtUserName " width = " 130 " color = " #000000 " /> </ mx:FormItem > < mx:FormItem label = " 密 码: " color = " #FFFFFF " > < mx:TextInput id = " txtPassword " width = " 130 " color = " #000000 " displayAsPassword = " true " /> </ mx:FormItem > < mx:FormItem label = "" > < mx:Button label
Flex Data Binding详解 Data BindIng简单的说就是当绑定源属性发生变化时,Flex 会把绑定源变化后属性的值赋给目的物的属性。做到了数据同步。
评论 0
收藏 0
赞 0
分享
关于Flex 初始化的research 前些天在写一个自定义的UI组件的时候,发现在override createChildren的,只能取到基本类型的自定义变量,而取不到Object类型的自定义变量。
评论 0
收藏 0
赞 0
分享
flex 简单例子(含实例效果图 源码) 要过节了,工作任务也完成了,闲来没事,研究svg,感觉市场上对svg支持度不高,导致了这项技术不能够被IT群众充分的接纳,一个没有人支持的技术,她必然也不会有很大的前景。研究来研究去,还是觉得flex比较好玩。
评论 0
收藏 0
赞 0
分享
Flex3 界面布局教程 国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
评论 0
收藏 0
赞 0
分享
Flex3 界面布局教程 第二篇 国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
评论 0
收藏 0
赞 0
分享
Flex 3 布局容器学习笔记 整理 国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
评论 0
收藏 0
赞 0
分享
查看更多