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天前
|
Linux iOS开发 开发者
跨平台开发不再难:.NET Core如何让你的应用在Windows、Linux、macOS上自如游走?
【8月更文挑战第28天】本文提供了一份详尽的.NET跨平台开发指南,涵盖.NET Core简介、环境配置、项目结构、代码编写、依赖管理、构建与测试、部署及容器化等多个方面,帮助开发者掌握关键技术与最佳实践,充分利用.NET Core实现高效、便捷的跨平台应用开发与部署。
30 3
|
3天前
|
缓存 Java API
【揭秘】.NET高手不愿透露的秘密:如何让应用瞬间提速?
【8月更文挑战第28天】本文通过对比的方式,介绍了针对 .NET 应用性能瓶颈的优化方法。以一个存在响应延迟和并发处理不足的 Web API 项目为例,从性能分析入手,探讨了使用结构体减少内存分配、异步编程提高吞吐量、EF Core 惰性加载减少数据库访问以及垃圾回收机制优化等多个方面,帮助开发者全面提升 .NET 应用的性能和稳定性。通过具体示例,展示了如何在不同场景下选择最佳实践,以实现更高效的应用体验。
16 3
|
3天前
|
前端开发 JavaScript 开发工具
跨域联姻:React.NET——.NET应用与React的完美融合,解锁前后端高效协作新姿势。
【8月更文挑战第28天】探索React.NET,这是将热门前端框架React与强大的.NET后端无缝集成的创新方案。React以其组件化和虚拟DOM技术著称,能构建高性能、可维护的用户界面;.NET则擅长企业级应用开发。React.NET作为桥梁,使.NET应用轻松采用React构建前端,并优化开发流程与性能。通过直接托管React组件,.NET应用简化了部署流程,同时支持服务器端渲染(SSR),提升首屏加载速度与SEO优化。
11 1
|
3天前
|
存储 缓存 安全
.NET 在金融行业的应用:高并发交易系统的构建与优化之路
【8月更文挑战第28天】在金融行业,交易系统需具备高并发处理、低延迟及高稳定性和安全性。利用.NET构建此类系统时,可采用异步编程提升并发能力,优化数据库访问以降低延迟,使用缓存减少数据库访问频率,借助分布式事务确保数据一致性,并加强安全性措施。通过综合优化,满足金融行业的严苛要求。
13 1
|
3天前
|
大数据 开发工具 开发者
从零到英雄:.NET核心技术带你踏上编程之旅,构建首个应用,开启你的数字世界探险!
【8月更文挑战第28天】本文带领读者从零开始,使用强大的.NET平台搭建首个控制台应用。无论你是新手还是希望扩展技能的开发者,都能通过本文逐步掌握.NET的核心技术。从环境搭建到创建项目,再到编写和运行代码,详细步骤助你轻松上手。通过计算两数之和的小项目,你不仅能快速入门,还能为未来开发更复杂的应用奠定基础。希望本文为你的.NET学习之旅开启新篇章!
12 1
|
3天前
|
Kubernetes Linux 开发者
【实战秘籍】从零开始:用.NET与Docker打造现代化容器化应用之旅
【8月更文挑战第28天】本文详细介绍如何使用 .NET 框架构建并部署 Docker 容器化应用程序,涵盖环境搭建、项目创建、Dockerfile 编写等关键步骤。首先安装必要软件,如 Visual Studio 2022 及 Docker Desktop。接着创建 .NET Core 控制台应用,并在项目根目录编写 Dockerfile 文件。使用 .NET 运行时基础镜像,复制二进制文件,指定入口点。运行命令构建镜像并测试容器。为实现通信,映射端口。最后,标签化镜像并推送到 Docker Hub,为生产环境部署做好准备。掌握这些步骤,即可轻松应对从小型项目到大规模应用的各种需求。
15 0
|
6天前
|
存储 Linux 网络安全
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
flex布局属性简介
flex布局属性简介
|
20天前
|
前端开发
FLex布局详解
Flex布局通过`flex-direction`定义主轴方向,包括横向`row`、反向横向`row-reverse`、纵向`column`及反向纵向`column-reverse`。`justify-content`控制主轴上子元素的排列,如起始端`flex-start`、末端`flex-end`、居中`center`、等间距`space-around`或两端对齐`space-between`。在Y轴上设置这些同样有效。`flex-wrap: wrap`使子元素在需要时换行。`align-items: center`实现侧轴(交叉轴)上的居中对齐
34 1
FLex布局详解
|
2天前
|
JavaScript
Vue3弹性布局(Flex)
这是一个基于 Vue 的弹性布局组件库,提供了丰富的参数配置,如宽度、方向、换行等,支持自定义对齐方式和间隙设置。在线预览展示了不同布局效果,包括单选、按钮和滑动输入条等组件的使用示例。
Vue3弹性布局(Flex)