• 1600 阅读
  • 1 回复

利用FluorineFx的RTMP实现消息的即时传递

视频在线上传+队列转换FLV+水印+捉图+修复+获时+转3GP(API语言不限,开视频站必备!)
本主题由 张小鱼 于 2010/10/12 18:47:00 执行 移动主题 [查看记录]
很多时候,我们需要实现即时消息,最典型的例如聊天室,当一个用户发送消息后,另外的用户能够马上就收到消息。方法有好几种,可能比较普遍的是不断地刷新页面,这个方法的缺点相信大家都知道,就不说了。现在我将讲另外一种方法,即利用FluorineFx的RTMP(实时消息协议)来实现即时消息。

先讲一下如何实现Flex客户端与.NET服务器的通信。实现结果是Flex客户端发送消息后,.NET服务器马上将消息转发到其它客户端,从而实现在线聊天记录的实时更新。以后会再讲如何以两个客户端,即Flex客户端和.NET客户端,通过.NET服务器来实现不同客户端之前的实时通信(这部分的研究目前还有一点问题)。

这些资料在网上一直找不到,都是从FluorineFx官方网的简单介绍中边看边研究的,希望对大家有用,也希望各位大虾能够提出更好的方法或建议。

(一)环境:安装了FluorineFx网关,Flex Builder 3,Visual Studio 2005或2008;

(二)建立.NET服务器端:

 1、新建解决方案,添加FluorineFx ASP.NET Web Site网站,再添加FluorineFx ServiceLibrary项目。如下所示:


您是游客您没有权限查看该图片

2、项目ServiceLibrary增加类文件MyChatApp.cs

在MyChatApp.cs中先添加两个简单的函数,一个是客户端连接服务器时的处理函数AppConnect(IConnection connection, object[] parameters),另一个是客户端断开服务器时的处理函数AppDisconnect(IConnection connection)。

简单贴一下代码:

代码 复制 - 运行


        public override bool AppConnect(IConnection connection, object[] parameters) 
 
        { 
 
            string userName = parameters[0] as string; //得到传送过来的用户名 
         
            connection.Client.SetAttribute("userName", userName);  //设置当前连接的客户端的userName属性值为传送过来的用户名,每个客户端的这个userName属性值不同 
 
            ISharedObject login_so = GetSharedObject(connection.Scope, "login");//得到在connection.Scope指定的作用域中,名为"login"的共享对象 
 
            if (login_so == null) 
 
            { 
 
                CreateSharedObject(connection.Scope, "login", false);  //当前共享对象不存在,则创建共享对象 
 
                login_so = GetSharedObject(connection.Scope, "login"); 
 
            } 
 
            login_so.SetAttribute(userName, userName);  //设置共享对象中的属性值,这目的是为了知道目前在线的有哪些人,这些用户名都存在共享对象中 
 
            return true; 
 
        } 
 
public override void AppDisconnect(IConnection connection) 
 
        { 
 
            string userName = connection.Client.GetAttribute("userName") as string;  //得到当前要断开连接的用户名 
 
            ISharedObject users_so = GetSharedObject(connection.Scope, "login");  //得到共享对象 
 
            if (users_so != null) 
 
            { 
 
                users_so.RemoveAttribute(userName);    //找到共享对象中的该用户,并移除它。 
 
            } 
 
            base.AppDisconnect(connection); 
 
        }

3、修改FluorineFx网站下WEB-INF/flex下的services-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}:1936" class="flex.messaging.endpoints.RTMPEndpoint"/> 
 
    </channel-definition> 
 
  </channels> 
 
</services-config>

4、在网站下添加文件夹:apps,在apps中再添加文件夹MyChatApp,最后在MyChatApp文件夹中添加配置文件app.config,文件内容如下。

代码 复制 - 运行


<?xml version="1.0" encoding="utf-8"?> 
 
<configuration> 
 
  <!-- Application object. Specify a fully qualified type name for your handler --> 
 
  <application-handler type="ServiceLibrary.MyChatApp"/> 
 
</configuration>

(三)建立Flex客户端:

1.在Flex Builder 3中新建一个Flex项目,新建一个MXML文件

代码 复制 - 运行


<?xml version="1.0" encoding="utf-8"?> 
 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> 
 
<mx:Script> 
 
    <![CDATA[ 
 
      import mx.collections.ArrayCollection; 
 
      import mx.controls.Alert; 
 
       
 
      private var login_nc:NetConnection; 
 
      public var login_ro:SharedObject;   
 
       
 
      private function loginClick():void{ 
 
          if(this.txtUserName.text!=""){ 
 
              this.login_nc=new NetConnection(); 
 
              login_nc.addEventListener(NetStatusEvent.NET_STATUS, netStatus); 
 
              login_nc.connect("rtmp://localhost:1936/MyChatApp",this.txtUserName.text); 
 
              this.login_nc.client=this; 
 
          }else{ 
 
              mx.controls.Alert.show("请输入用户名!"); 
 
          } 
 
      } 
                 
          private function netStatus(event:NetStatusEvent):void  
 
          { 
 
                var info:Object = event.info; 
 
 
 
              switch (info.code)  
 
              { 
 
                  case "NetConnection.Connect.Success" : 
 
                    login_ro = SharedObject.getRemote("login", login_nc.uri); 
 
                    if(login_ro){ 
 
                        this.viewstack1.selectedIndex=1; 
 
                        login_ro.addEventListener(SyncEvent.SYNC, OnSync); 
 
                        login_ro.connect(login_nc); 
 
                        login_ro.client = this; 
 
                    } 
 
                    break; 
 
                  case "NetConnection.Connect.Failed" : 
 
                    mx.controls.Alert.show("连接失败!"); 
 
                  break; 
 
                  default : 
 
                    //statements 
 
                    break; 
 
              } 
 
          } 
 
           
 
          private function OnSync(event:SyncEvent):void  
 
          { 
 
              var info:Object; 
 
              var currentIndex:Number; 
 
              var currentNode:Object; 
 
              var changeList:Array = event.changeList; 
 
              var temp:Array = new Array(); 
 
              for(var p:String in login_ro.data){ 
 
                  if(login_ro.data[p]!=""){  
 
                    temp.push(login_ro.data[p]); 
 
                  } 
 
              } 
 
              this.usersList.dataProvider=temp; 
 
          } 
 
           
 
          private function disconnect():void 
 
            { 
 
              login_nc.close(); 
 
              this.viewstack1.selectedIndex=0; 
 
            } 
 
           
 
    ]]> 
 
</mx:Script> 
 
    <mx:ViewStack id="viewstack1" width="100%" height="100%" creationPolicy="all"> 
 
      <mx:Canvas label="View 1" width="100%" height="100%"> 
 
          <mx:Button x="210" y="110" id="btnLogin" label="登录" click="loginClick()" /> 
 
          <mx:TextInput x="210" y="70" id="txtUserName"/> 
 
      </mx:Canvas> 
 
      <mx:Canvas label="View 1" width="100%" height="100%"> 
 
          <mx:TextArea x="251" y="57" width="714" height="336" id="area_LoginMes"/> 
 
          <mx:Label x="26" y="29" text="当前登录用户:" width="98"/> 
 
          <mx:List width="134" height="378" x="26" y="55" id="usersList"/> 
 
          <mx:Button x="55" y="456" label="注销" click="disconnect()"/> 
 
      </mx:Canvas> 
 
    </mx:ViewStack> 
 
</mx:Application>

当用户输入用户名,并点击登录按钮时,就会调用login_nc.connect函数连接到服务器端,login_nc同时增加了一个连接状态的监听,当连接成功后,Flex将连接并得到相应的ShareObject共享对象,并对共享对象添加了一个同步监听。即将有别的用户登录时,服务器端会将用户名存储到这个共享对象中,这时其它客户端就会同时监听到共享对象的变化,并即时地更新信息。

这样就实现了当有一个用户登录时,其它用户的登录列表就能实时地进行更新。
您是游客您没有权限查看该图片

这是三个不同用户登录后的情况,还是要各位在本机上测试才能看到那个实时更新的效果。

很多讲得并不详细,大概大家也应该看得懂,以后再慢慢补充完整。


本帖最后由 张小鱼 于 2009-12-01 13:14 编辑
小鱼的淘宝店铺-多多支持哇
视频在线上传+队列转换FLV+水印+捉图+修复+获时+转3GP(API语言不限,开视频站必备!)
最近都比较忙,所以就还没有更新。上次是讲到如果Flex如果连接VS服务器,并实实现在线人员列表时查看和实时更新,现在讲一下Flex客户端如果发送信息,并传送给其他所有的客户端。主要原理是Flex客户端发送信息到VS服务端,再由VS服务器端指定Flex客户端中的方法去接收数据,也就是VS服务器端将信息广播到所有Flex客户端。

在Flex客户端中添加方法

代码 复制 - 运行


          private function sendMessage():void{ 
 
              login_nc.call("sendMessage",null,"大家好!"); 
 
          } 
 
       
 
          public function receiveMessage(msg:String):void{ 
 
              Alert.show(msg); 
 
          }

sendMessage()指定了要调用的VS服务器中的方法,就是“sendMessage”,这个方法是在VS中定义的,login_nc是上次讲到的那个远程连接。

receiveMessage(msg::String)是Flex客户端接收到数据后,要执行的方法。这个方法是供VS服务器端调用响应的。

在VS服务器端添加方法

代码 复制 - 运行


        public void sendMessage(IConnection connection, string msg) 
 
        { 
 
            string userName = connection.Client.GetAttribute("userName") as string; 
 
            msg = userName + ": " + msg; 
 
            ISharedObject users_so = GetSharedObject(connection.Scope, "login"); 
 
            users_so.SendMessage("receiveMessage", new object[] { msg }); 
 
        }

sendMessage(IConnection connection, string msg)就是在Flex客户端中调用的方法:login_nc.call("sendMessage",null,"大家好!"),这里将对Flex传送过来的信息进行处理。connection.Client.GetAttribute("userName") as string;可以得到当前连接调用该方法的用户是谁,上次在登录的时候,我们已经将这个值保存了下来。GetSharedObject(connection.Scope, "login")得到远程共享对象,然后利用这个远程共享对象发送信息,信息的接收方法是Flex中的receiveMessage方法,也就是这句users_so.SendMessage("receiveMessage", new object[] { msg });

这个实现过程还是比较容易理解的。

结果如下:如果大家在不同机上登录,会看到比较明显的效果。
您是游客您没有权限查看该图片
小鱼的淘宝店铺-多多支持哇