• 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[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="登陆服务器" 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>
    复制代码
  • 相关阅读:
    C++开源库
    Boost ASIO proactor 浅析
    ehcache
    http://www.servicestack.net/
    hortonworks
    (总结)Nginx 502 Bad Gateway错误触发条件与解决方法
    VMware vSphere虚拟化学习手册下部
    精确字符串匹配(BM算法) [转]
    Linux下基本TCP socket编程之服务器端
    How To Use Linux epoll with Python
  • 原文地址:https://www.cnblogs.com/tianlangshu/p/2467110.html
Copyright © 2020-2023  润新知