FluorineFx + Flex视频聊天室案例开发----服务器端

FluorineFx + Flex视频聊天室案例开发----服务器端

      在我之前的一篇文章里曾经介绍过FluorineFx的ApplicationAdapter的基本步骤,以及使用FluorineFx的ApplicationAdapter来开发及时通信应用的相关知识点。ApplicationAdapter应用最终需要部署到支持FluorineFx服务的Web应用(ASP.NET网站)上,如下图则表示了FluorineFx应用的目录结构。

                  

       对于使用过FluorineFx开发应用的朋友来说,这个图在熟悉不过了。要使用FluorineFx开发及时通信应用,项目结构就必须这样搭建,首先看看本篇案例的解决方案截图:

                  

      从上面的解决方案截图可以看到,整个项目由两部分构成,一个是FluorineFx的类库,封装了远程服务(RemotingService)接口和及时通信应用程序(ApplicationAdapter)。另一个则是FluorineFx网站(和ASP.NET网站没多大的区别,不同的是其中加入了FluorineFx的一些相关配置)。

      为了方便演示案例本篇就直接使用的 Access做为数据库,根据数据库的字段结构建立了一个数据传输对象(DTO),同时为该DTO对象标记[FluorineFx.TransferObject],以使其可以作为FluorineFx的数据传输对象使用。

namespace  ChatRoom.Services.DTO
{
    
///   <summary>
    
///  用户信息(数据传输对象)
    
///   </summary>
    [FluorineFx.TransferObject]
    
public   class  UserInfo
    {
        
///   <summary>
        
///  构造方法
        
///   </summary>
         public  UserInfo() { }

        
public   int  ID {  get set ; }

        
public   string  UserName {  get set ; }

        
public   string  NickName {  get set ; }

        
public   string  Password {  get set ; }

        
public   string  HeadImage {  get set ; }
    }
}

 

      远程服务接口(DataService)提供了用户注册,登陆等最基本的通信接口方法,在了解通信接口之前首先学习一个工具类,该类的提供了一个将DataTable类型数据转话为IList<Object>类型返回,详细代码如下:

namespace  ChatRoom.Services.Utils
{
    
public   static   class  ConvertUtils
    {
        
///   <summary>
        
///  提供将DataTable类型对象转换为List集合
        
///   </summary>
        
///   <param name="table"></param>
        
///   <returns></returns>
         public   static  List < T >  ConvertToList < T > (DataTable table)  where  T :  new ()
        {
            
// 置为垃圾对象
            List < T >  list  =   null ;

            
if  (table  !=   null )
            {
                DataColumnCollection columns 
=  table.Columns;
                
int  columnCount  =  columns.Count;
                T type 
=   new  T();
                Type columnType 
=  type.GetType();
                PropertyInfo[] properties 
=  columnType.GetProperties();

                
if  (properties.Length  ==  columnCount)
                {
                    list 
=   new  List < T > ();
                    
foreach  (DataRow currentRow  in  table.Rows)
                    {
                        
for  ( int  i  =   0 ; i  <  columnCount; i ++ )
                        {
                            
for  ( int  j  =   0 ; j  <  properties.Length; j ++ )
                            {
                                
if  (columns[i].ColumnName  ==  properties[j].Name)
                                {
                                    properties[j].SetValue(type, currentRow[i], 
null );
                                }
                            }
                        }
                        list.Add(type);
                        type 
=   new  T();
                    }
                }
                
else
                {
                    list 
=   null ;
                }
            }
            
else
            {
                
throw   new  ArgumentNullException( " 参数不能为空 " );
            }
            
return  list;
        }
    }
}

      

      下面是使用FluorineFx提供的远程服务接口的详细定义:

namespace  ChatRoom.Services
{
    [RemotingService]
    
public   class  DataService
    {
        
public  DataService()
        {
        }

        
///   <summary>
        
///  用户注册
        
///   </summary>
        
///   <param name="info"></param>
        
///   <returns></returns>
         public   bool  Register(UserInfo info)
        {
            info.Password 
=  FormsAuthentication.HashPasswordForStoringInConfigFile(info.Password,  " MD5 " );
            StringBuilder CommandText 
=   new  StringBuilder( " insert into UserInfo values ( " );
            CommandText.Append(
string .Format( " '{0}','{1}','{2}','{3}') " , info.UserName, info.Password, info.NickName, info.HeadImage));

            
try
            {
                
int  row  =  DataBase.ExecuteSQL(CommandText.ToString());
                
if  (row  >   0 )
                    
return   true ;
                
return   false ;
            }
            
catch  (Exception ex)
            {
                
return   false ;
            }
        }

        
///   <summary>
        
///  用户登录
        
///   </summary>
        
///   <param name="info"></param>
        
///   <returns></returns>
         public  UserInfo Login(UserInfo info)
        {
            info.Password 
=  FormsAuthentication.HashPasswordForStoringInConfigFile(info.Password,  " MD5 " );
            StringBuilder CommandText 
=   new  StringBuilder( " select * from UserInfo where  " );
            CommandText.Append(
string .Format( " UserName='{0}' and Password='{1}' " , info.UserName, info.Password));

            
try
            {
                DataTable table 
=  DataBase.ExecuteQuery(CommandText.ToString());
                
if  (table  !=   null )
                {
                    
return  ConvertUtils.ConvertToList < UserInfo > (table)[ 0 ];
                }
                
return   null ;
            }
            
catch  (Exception ex)
            {
                
return   null ;
            }
        }
    }
}

 

      FluorineFx.Messaging.Adapter.ApplicationAdapter做为新的应用程序的基类,提供了客户端与应用程序之间通信的接口方法、数据流服务以及共享对象服务等。它能够时时处理应用程序的启动、停止、客户端的连接和断开等及时操作。

      在本篇案例项目中,我所建立的ApplicationAdapter为ChatRoomApplication,用来提供启动及时应用服务器,接受客户端连接和断开以及视频流服务等功能。这里需要注意一点,在接受客户端连接的时候业务处理流程,在应用程序启动的时候就将会创建一个共享对象,用来保存在线用户列表。

public   override   bool  AppStart(IScope application)
{
    
// 用户共享对象
    CreateSharedObject(application,  " OnLineUsers " false );

    
return   base .AppStart(application);
}

 

      当用户通过客户端连接服务器的时候,将用户信息传递到服务器,服务器端接受到数据后将会对用户数据进行验证(验证用户名和密码是否匹配),如果验证失败则直接返回false表示连接服务器失败,如果验证通过则设置当前连接的属性并连接到共享对象,然后取出共享对象里的在线用户列表数据,判断当前连接到服务器的用户是否在线,如果用户已在线则返回false表示用户登录失败,并给于提示当前用户已经登录。如果用户没有存在于共享里则表示当前连接的用户没有登录,那么就将当前连接的用户信息写入共享对象,客户端会通过异步事件处理函数接收到最新的在线用户列表数据。

 

 

public   override   bool  AppConnect(IConnection connection,  object [] parameters)
{
    UserInfo info 
=  ((Dictionary < string object > )parameters[ 0 ])[ " 0 " as  UserInfo;

    connection.Client.SetAttribute(
" UserName " , info.UserName);

    
// 连接到共享对
    ISharedObject users  =  GetSharedObject(connection.Scope,  " OnLineUsers " );
    
if  (users  ==   null )
    {
        
this .CreateSharedObject(connection.Scope,  " OnLineUsers " false );
        users 
=  GetSharedObject(connection.Scope,  " OnLineUsers " );
    }
    
else
    {
        List
< UserInfo >  onLineUserList  =  users.GetAttribute( " UserInfo " as  List < UserInfo > ;
        
if  (onLineUserList  !=   null )
        {
            List
< UserInfo >  tempList  =  (from u  in  onLineUserList
                                       
where  u.UserName  ==  info.UserName
                                       select u
                                       ).ToList();
            
if  (tempList.Count  >   0 )
            {
                
// Server RPC
                 this .CallClientMethod(connection,  " onLogined " " 当前用户已经登录 " );
                
return   false ;
            }
            
else
            {
                onLineUserList.Add(info);
                users.SetAttribute(
" UserInfo " , onLineUserList);
            }
        }
        
else
        {
            onLineUserList 
=   new  List < UserInfo >  { info };
            users.SetAttribute(
" UserInfo " , onLineUserList);
        }
    }
    
return   base .AppConnect(connection, parameters);
}

 

      OK,到这里就完成了对用户进行有效验证和接受与拒绝连接的功能,那么如果是用户下线了或是无意之间关闭了浏览器呢?这种情况应当如何去处理,如果不处理的话那么用户是不是会一直卡在线上呢?答案是肯定的。FluorineFx为此也提供了接口来处理用户下线的操作,那么在这里我们需要做什么呢?需要做的就是取出当前退出系统的客户端连接属性值,然后在取出共享对象里的在线用户列表数据,根据用户连接时所设置的属性进行循环判断,找到当前退出系统的用户数据,最后将他从共享对象里删除,服务器端的共享对象一但改变,所有连接到服务器的客户端都会通过共享对象的异步事件得到最新的数据,用来更新客户端的显示列表。

public   override   void  AppDisconnect(IConnection connection)
{
    
string  userName  =  connection.Client.GetAttribute( " UserName " as   string ;

    ISharedObject users 
=  GetSharedObject(connection.Scope,  " OnLineUsers " );

    
if  (users  !=   null )
    {
        List
< UserInfo >  onLineUserList  =  users.GetAttribute( " UserInfo " as  List < UserInfo > ;
        
if  (onLineUserList  !=   null )
        {
            List
< UserInfo >  tempList  =  onLineUserList.FindAll(c  =>  c.UserName.Length  >   0 );
            
foreach  (var item  in  tempList)
            {
                
if  (item.UserName.Equals(userName))
                {
                    onLineUserList.Remove(item);
                }
            }
            users.SetAttribute(
" UserInfo " , onLineUserList);
        }
    }
    
base .AppDisconnect(connection);
}

 

      到这里服务器端的开发就基本完成了,现在就需要将这个ApplicationAdapter配置好,然后启动通过网站启动服务,Flex或者是Flash就可以通过rtmp协议连接了。首先需要配置的是rtmp协议通信信道:

< channel-definition  id ="my-rtmp"  class ="mx.messaging.channels.RTMPChannel" >
    
< endpoint  uri ="rtmp://{server.name}:2777"  class ="flex.messaging.endpoints.RTMPEndpoint" />
    
< properties >
        
< idle-timeout-minutes > 20 </ idle-timeout-minutes >
    
</ properties >
</ channel-definition >

 

      然后就是在应用目录下面配置ApplicationAdapter,指定当前这个应用又那一个Adapter来处理。建立app.config文件进行如下配置:

<? xml version="1.0" encoding="utf-8" ?>
< configuration >
  
< application-handler  type ="ChatRoom.Services.ChatRoomApplication" />
</ configuration >

 

      在下一篇中我将详细介绍客户端的开发,包括使用RTMP协议连接到服务器,使用远程共享对象做在线用户列表和聊天消息发送,以及通过NetStream建立视频流连接实现视频聊天等相关功能。

 

版权说明

  本文属原创文章,欢迎转载,其版权归作者和博客园共有。  

  作      者:Beniao

 文章出处:http://beniao.cnblogs.com/  或  http://www.cnblogs.com/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值