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

简介:
      远程共享对象(Remote Shared Objects) 可以用来跟踪、存储、共享以及做多客户端的数据同步操作。只要共享对象上的数据发生了改变,将会把最新数据同步到所有连接到该共享对象的应用程序客户端。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 = " 登陆服务器 "  click = " connectionServer(event) "  
                enabled
= " {this.txtUserName.text.length&gt;0?true:false} "  color = " #FFFFFF " />
        
</ mx:FormItem >
    
</ mx:Form >
    
< mx:TextArea x = " 24 "  y = " 174 "  width = " 288 "  height = " 153 "  alpha = " 1.0 "  
        backgroundColor
= " #F2D2D2 "  backgroundAlpha = " 0.26 "  color = " #FFFFFF "  
        id
= " txtTraceArea "  borderColor = " #FFFFFF " />
</ mx:Application >




本文转自 beniao 51CTO博客,原文链接:http://blog.51cto.com/beniao/163534,如需转载请自行联系原作者

目录
相关文章
|
3月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
181 10
|
3月前
|
开发者 容器
flex 布局属性在实际项目中的应用场景有哪些?
flex 布局属性在实际项目中的应用场景有哪些?
|
1月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
1月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
106 2
|
3月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
164 6
|
4月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
72 0
|
4月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
81 0
|
4月前
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
50 0
|
27天前
|
容器
Bootstrap5 Flex(弹性)布局4
排序:.order 类可设置弹性子元素的排序,范围从 .order-1 至 .order-12,数字越小优先级越高。外边距:.ms-auto 和 .me-auto 分别用于设置子元素的右侧和左侧外边距为 auto。包裹:.flex-nowrap(默认)、.flex-wrap 和 .flex-wrap-reverse 用于控制弹性容器中的子元素是否换行及换行方向。
|
2月前
|
前端开发 UED 容器
使用 Flex 布局实现垂直居中效果
【10月更文挑战第7天】
290 57