当前位置: 首页 > news >正文

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

远程共享对象(Remote Shared Objects)可以用来跟踪、存储、共享以及做多客户端的数据同步操作。只要共享对象上的数据发生了改变,将会把最新数据同步到所有连接到该共享对象的应用程序客户端。FluorineFx所提供的远程共享对象(Remote Shared Objects)和FMS的共享对象的功能是一样,对于熟悉FMS开发的朋友来说,学习FluorineFx的远程共享对象是非常简单的。

共享对象可以在服务器端创建,也可以在客户端创建。在客户端创建共享对象的方法和使用FMS开发是一样的,创建一个NetConnection对象,通过该对象的connect()方法连接到服务器,然后通过SharedObject.getRemote()方法就可以在客户端创建一个远程共享对象。如下实例代码:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> private functionconnectionServer(): void
{
varnc:NetConnection
= new NetConnection();
nc.connect(
" rtmp://localhost:1617/SOAPP " , " username " , " password " )
nc.addEventListener(NetStatusEvent.NET_STATUS,onStatusHandler);
nc.client
= this ;
}

private functiononStatusHandler( event :NetStatusEvent): void
{
if ( event .info.code == " NetConnectin.Connect.Success " )
{
createSharedObject();
}
}

private functioncreateSharedObject(): void
{
varso:SharedObject
= SharedObject.getRemote( " OnLineUsers " ,nc.uri, false );
so.addEventListener(SyncEvent.SYNC,onSyncHandler);
so.connect(
this .nc);
so.client
= this ;
}

private functiononSyncHandler( event :SyncEvent): void
{
// ..doother
}

在FluorineFx的服务器端创建远程共享对象和FMS有很大的区别,FluorineFx的ISharedObjectService接口提供了专门用于创建远程共享对象的方法CreateSharedObject(),ApplicationAdapter实现了此接口方法。定义如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public bool CreateSharedObject(IScopescope, string name, bool persistent)
{
ISharedObjectServiceservice
= (ISharedObjectService)ScopeUtils.GetScopeService(scope, typeof (ISharedObjectService));
return service.CreateSharedObject(scope,name,persistent);
}

如果要在服务器端创建远程共享对象,直接调用ApplicationAdapter类中的CreateSharedObject()方法就可以。如下在FluorineFx服务器端创建远程共享对象的代码块:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> ISharedObjectusers_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的相关方法来实现应用程序的不同需求,详细如下代码块:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> 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>
/// <paramname="application"></param>
/// <returns></returns>
public override bool AppStart(IScopeapplication)
{
Trace.WriteLine(
" 应用程序启动 " );
return true ;
}

/// <summary>
/// 房间启动
/// </summary>
/// <paramname="room"></param>
/// <returns></returns>
public override bool RoomStart(IScoperoom)
{
Trace.WriteLine(
" 房间启动 " );
if ( ! base .RoomStart(room))
return false ;
return true ;
}

/// <summary>
/// 接收客户端的连接
/// </summary>
/// <paramname="connection"></param>
/// <paramname="parameters"></param>
/// <returns></returns>
public override bool AppConnect(IConnectionconnection, 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)
ISharedObjectusers_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>
/// <paramname="client"></param>
/// <paramname="room"></param>
/// <returns></returns>
public override bool RoomJoin(IClientclient,IScoperoom)
{
Trace.WriteLine(
" 加入房间 " + room.Name);
return true ;
}

/// <summary>
/// 离开房间
/// </summary>
/// <paramname="client"></param>
/// <paramname="room"></param>
public override void RoomLeave(IClientclient,IScoperoom)
{
Trace.WriteLine(
" 离开房间 " + room.Name);
base .RoomLeave(client,room);
}

/// <summary>
/// 用户退出
/// </summary>
/// <paramname="connection"></param>
public override void AppDisconnect(IConnectionconnection)
{
string userName = connection.Client.GetAttribute( " userName " ) as string ;
ISharedObjectusers_so
= GetSharedObject(connection.Scope, " OnLineUsers " );
if (users_so != null )
{
// 从共享对象中移除当前退出系统用户
users_so.RemoveAttribute(userName);
}
base .AppDisconnect(connection);
}
}
}

开发好了ApplicationAdapter,还需要对此ApplicationAdapter进行通信配置,在FluorineFx的应用程序目录中添加app.config并进行如下配置:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> <? xmlversion="1.0"encoding="utf-8" ?>
< configuration >
< application-handler type ="Fx.Adapter.MyApp" />
</ configuration >

另外还需要配置一个客户端方法的通信通道,通过FluorineFx网站下的WEB-INF/flex/service-config.xml配置:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><?xml version="1.0" encoding="utf-8" ?>
<services-config>

<channels>

<channel-definitionid="my-rtmp"class="mx.messaging.channels.RTMPChannel">
<endpointuri="rtmp://{server.name}:1617"class="flex.messaging.endpoints.RTMPEndpoint"/>
</channel-definition>

</channels>

</services-config>

如上便完成了服务器端的开发,在flash/felx客户端通过NetConnection去连接应用,并根据当前的连接去连接服务器端的远程共享对象,最后通过异步事件来实现数据同步更新。如下程序运行截图:

此时开多个浏览器窗口测试,不同窗口使用不同的用户名登录,可以很清楚的看到,我们已经实现了在线用户的数据同步功能,可以及时的反映用户上线离线,可以及时的同步在线用户列表的数据。

另外远程共享对象还有一个功能非常强大的特性方法,就是连接到共享对象的客户端之间可以直接广播消息(客户端调用客户端的方法)。就以上面在线用户的案例为例,用户成功登陆服务器我需要广播一条消息,用户退出了我也需要广播一条消息,要实现这个功能就需要通过远程共享的客户端呼叫(send()方法)来实现,如下代码块:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> private functiononCallClient(message:String): void
{
so.send(
" onSayMessage " ,message);
}

远程共享对象的send()方法调用了onSayMessage这个客户端方法来实现对连接到共享对象上的所有客户端广播消息,那么我们的在定义一个onSayMessage方法,如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->/**
*接受客户端呼叫---此方法必须是public修饰
*/
publicfunctiononSayMessage(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客户端的完整代码:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> <? xmlversion = " 1.0 " encoding = " utf-8 " ?>
< mx:Applicationxmlns: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[
importmx.controls.Alert;
importdotnet.fluorinefx.VO.UserInfo;

private varnc:NetConnection;
private varso:SharedObject;
private varinfo:UserInfo;

private functionconnectionServer( 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 functiononStatusHandler( 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(
" 用户【<fontcolor=\ " #4100b9\ " > " + info.UserName + " </font>】登陆了系统! " );
}
}

private functiononSyncHandler( event :SyncEvent): void
{
vartemp:Array
= new Array();
for (varu:String in so.data)
{
// traceWriteln("异步事件->共享对象:"+u+":"+so.data[u]);
temp.push(so.data[u]);
}
this .userList.dataProvider = temp;
}

private functiontraceWriteln(param:String): void
{
txtTraceArea.htmlText
+= param + " \n " ;
txtTraceArea.validateNow();
txtTraceArea.verticalScrollPosition
= txtTraceArea.maxVerticalScrollPosition;
}

private functiononCallClient(message:String): void
{
so.send(
" onSayMessage " ,message);
}
/* *
*接受客户端呼叫
*/
public functiononSayMessage(message:Object): void
{
traceWriteln(message.toString());
}
]]
>
</ mx:Script >
< mx:Labelx = " 24 " y = " 134 " id = " connStatus " width = " 288 " color = " #FFFFFF " />
< mx:Listx = " 342 " y = " 10 " height = " 347 " width = " 160 " id = " userList " >
</ mx:List >
< mx:Formx = " 24 " y = " 10 " width = " 236 " >
< mx:FormItemlabel = " 用户名: " color = " #FFFFFF " >
< mx:TextInputid = " txtUserName " width = " 130 " color = " #000000 " />
</ mx:FormItem >
< mx:FormItemlabel = " 密码: " color = " #FFFFFF " >
< mx:TextInputid = " txtPassword " width = " 130 "
color
= " #000000 " displayAsPassword = " true " />
</ mx:FormItem >
< mx:FormItemlabel = "" >
< mx:Buttonlabel = " 登陆服务器 " click = " connectionServer(event) "
enabled
= " {this.txtUserName.text.length&gt;0?true:false} " color = " #FFFFFF " />
</ mx:FormItem >
</ mx:Form >
< mx:TextAreax = " 24 " y = " 174 " width = " 288 " height = " 153 " alpha = " 1.0 "
backgroundColor
= " #F2D2D2 " backgroundAlpha = " 0.26 " color = " #FFFFFF "
id
= " txtTraceArea " borderColor = " #FFFFFF " />
</ mx:Application >

相关文章:

  • 简单说说什么是Restful
  • Flex与.NET互操作(十三):FluorineFx.Net实现视频录制与视频回放
  • Swift中的本地化实现
  • Azure .Net应用架构原型
  • Nebula3中的委托(Delegate)
  • 使用微软T4 template进行代码生成
  • ora-12537教训
  • Asp.Net MVC webAPI Token based authentication
  • Asp.Net 2.0 TreeView的Checkbox级联操作
  • azure最佳实践系列1-自我修复的设计
  • Nebula3 RTTI 小技巧
  • azure 最佳实践 -- 保持冗余
  • Swift 3 打印功能
  • 还需要编写DAO代码吗?SQL+接口就足够了!(Part 1)
  • 百度关闭竞价排名部分功能,以提升用户体验
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • CentOS从零开始部署Nodejs项目
  • ComponentOne 2017 V2版本正式发布
  • CSS中外联样式表代表的含义
  • Javascript Math对象和Date对象常用方法详解
  • Javascript基础之Array数组API
  • leetcode讲解--894. All Possible Full Binary Trees
  • magento2项目上线注意事项
  • nodejs:开发并发布一个nodejs包
  • Object.assign方法不能实现深复制
  • PAT A1120
  • Redux系列x:源码分析
  • Spring Boot快速入门(一):Hello Spring Boot
  • Vultr 教程目录
  • webpack4 一点通
  • Yeoman_Bower_Grunt
  • 百度地图API标注+时间轴组件
  • 搞机器学习要哪些技能
  • 精彩代码 vue.js
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • 用Canvas画一棵二叉树
  • 在weex里面使用chart图表
  • 中文输入法与React文本输入框的问题与解决方案
  • 追踪解析 FutureTask 源码
  • 【干货分享】dos命令大全
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • # centos7下FFmpeg环境部署记录
  • (04)odoo视图操作
  • (5)STL算法之复制
  • (LeetCode 49)Anagrams
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (三) diretfbrc详解
  • (三)模仿学习-Action数据的模仿
  • (一) springboot详细介绍
  • (转)一些感悟
  • . Flume面试题