Flex与.NET互操作(十二):FluorineFx.Net的及时通信应用(Remote Shared Objects)(三)

所属分类: 网页制作 / Flash 阅读数: 2103
收藏 0 赞 0 分享

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[0as string;
            
string password = parameters[1as 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 放大镜效果实现代码

Flex 放大镜效果实现代码
收藏 0 赞 0 分享

Flex 读取xml文件的方法

Flex 读取xml文件的多种方法小结,大家可以根据需要自己选择。
收藏 0 赞 0 分享

Flex 性能优化常用手法总结

随着Flex越来越多的被人们所熟知,越来越多的互联网也开始了RIA应用。
收藏 0 赞 0 分享

Flex Data Binding详解

Data BindIng简单的说就是当绑定源属性发生变化时,Flex 会把绑定源变化后属性的值赋给目的物的属性。做到了数据同步。
收藏 0 赞 0 分享

Flex 全屏组件 部分全屏的实现代码

一般情况下,Flex全屏是指将整个舞台全屏,而不是待定的组件全屏.网上的例子也一般是指这种情况的.
收藏 0 赞 0 分享

关于Flex 初始化的research

前些天在写一个自定义的UI组件的时候,发现在override createChildren的,只能取到基本类型的自定义变量,而取不到Object类型的自定义变量。
收藏 0 赞 0 分享

flex 简单例子(含实例效果图 源码)

要过节了,工作任务也完成了,闲来没事,研究svg,感觉市场上对svg支持度不高,导致了这项技术不能够被IT群众充分的接纳,一个没有人支持的技术,她必然也不会有很大的前景。研究来研究去,还是觉得flex比较好玩。
收藏 0 赞 0 分享

Flex3 界面布局教程

国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
收藏 0 赞 0 分享

Flex3 界面布局教程 第二篇

国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
收藏 0 赞 0 分享

Flex 3 布局容器学习笔记 整理

国庆期间,做了不少基于 flex 的开发工作,对 flex 的布局容器有了进一步深入的理解,也找到不少非常棒的文章,分享到这里方便一下大家。
收藏 0 赞 0 分享
查看更多