`
hereson
  • 浏览: 1453897 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

一步一步学Flash Media Server

    博客分类:
  • fms2
阅读更多

从今天起,我们来学习一下 Flash Media Server,简称FMS,从名字上我们可以看出这是一个媒体服务器,其它简单点理解就是一个交互服务器,而且可以实现媒体方面的交互,我们可以利用它来完成一些网络版的FLASH程序,比如聊天室,网络版的不是很复杂的游戏,等等。

 

大家可以到如下地址下载最新的3.0版本:

http://www.adobe.com/products/flashmediainteractive/

 

当然这个版本是开发版,开发版和正式版的区别在于开发版只支持10个连接数,不过对于我们学习已经是足够了.

 

大家安装完之后,我们会在安装目录里看到applicationslogs这两个目录,这个是我们用的最多的两个目录,applications是我们放服务端程序的地方,logs是放日志文件的地方,日志文件对我们调试程序很有帮助.

 

大家也可以修改这两个文件的路径,当然要更改配置文件,conf目录里放的都是配置文件,我们打开fms.ini这个文件我们会找到

 

VHOST.APPSDIR =

LOGGER.LOGDIR =

这两项就是更改applicationslogs路径的地方,如比我们改成:

 

VHOST.APPSDIR = e: \fms\applications

LOGGER.LOGDIR = e:\fms\logs

 

那么我们就在e盘建立相应的目录把程序入在里面就可以了.

 

确定FMS的服务打开了,就可以运行了,FMS有两个服务,在安装完后在开始菜单我们可以找到Start Flash Media Administration Server 3 Start Adobe Flash Media Server 3 这两个打开服务的菜单,要打开服务点这两个就可以了,前一个是FMS管理服务,后一个是FMS服务,Stop Flash Media Administration Server 3Stop Adobe Flash Media Server 3就是关闭这个服务的,默认状态下,在电脑刚启动里,这两个服务就会启动,当然也可以在windows的服务里面来启动和关闭服务.

 

Flash Media Administration Console这个菜单项是打开管理后台,管理后台对我们调试程序是很有作用的,打开这个之后是一个登录界面,要求输入用户名密码和服务器地址,用户名和密码是我们在安装FMS就输入的,服务器地址就是我们要连的装有FMS的地址,我们连接本机就用localhost或者127.0.0.1就可以了,如果我们在安装的时候改了它的默认端口(1935)的话,我们在地址上还要加端口,如果没有改端口这里就不用加了,输入用户名密码和服务器地址,我们就可以进入管理界面了,具体如何操作,我们在后面会讲到.

 

整个教程,我会用一个聊天室的例子来讲解如何使用FMS,当然我只能讲出我所知道的知识,不能完全讲出FMS的强大的功能.

 

下节我们接触FMS基本的结构,下节继续.

 

 

 

 

 

 

今天我们来看一下FMS里面最重要的一个类---Application.

 

Application类包含了有关一个Flash Media Server应用程序实例的信息,这些信息会一直维持直至这个应

用程序实例被卸载.

 

先看一下在Application类中常用的方法:

 

Application.acceptConnection()      接受一个来自客户机的至一个应用程序的连接。

Application.disconnect()                  从服务器断开一个客户机的连接。

Application.rejectConnection()       拒绝至一个应用程序的连接。

Application.shutdown()                    卸载应用程序实例。

 

再看一下在Application类中常用的属性:

 

Application.clients      一个对象,该对象包含了当前连接到这个应用程序的所有客户的一个列表。

Application.name       一个应用程序实例的名字。

 

最后看一下在Application类中常用的事件:

 

Application.onAppStart              当这个应用程序被服务器装载时调用。

Application.onAppStop              当这个应用程序被服务器卸载时调用。

Application.onConnect              当一个客户机连接到这个应用程序时调用。

Application.onDisconnect        当一个客户机从这个应用程序断开连接时调用。

 

 

 

(这些是比较常用的,还有其它一些,大家可以看FMS自带的文档)

 

 

 

我们来描述一下客户机连接的流程:

 

 

 

1.当第一个用户连接的时候,会启动服务端应该程序实例,这样就会触发Application.onAppStart事件,通常我们会里这个事件里做一些初如化的事情.

 

2.用户连接的时候会触发Application.onConnect事件,在这个事件里我们可以用Application.acceptConnection()方法来接受这个用户的连接,也可以用Application.rejectConnection()方法拒绝这个用户.

 

3.服务器想把一个用户断开的时候,可以用Application.disconnect()方法,当一个用户断开,不管是服务器把它断开,还是客户端自己断开,都会触发Application.onDisconnect事件.

 

4.使用Application.shutdown()方法可以卸载应用程序实例,当这个应该程序长时间(大概半个小时左右)没有客户端连接的时候,应该程序也会被卸载,当应该程序卸载的时候会触发Application.onAppStop事件.

 

 

我们写FMS程序时,大多数都是这样的流程.

 

 

 

 

一步一步学Flash Media Server()

今天我们来看一下用AS 3连接FMS3的代码(这些代码其它对FMS2也是适用的).

 

这个例子我们不会去写FMS的代码,但我们需要建一个FMS应该程序,其实就是建一个目录,FMS放应该程序的目录(applications)里建一个文件夹,我们后面要做聊天室的例子,所以我们就建一个名叫chat的目录.

 

 

接下来就是客户端的代码了,我们建一个chat.flaFLASH文件,再建一个文档类Chat.as:

 

package net.smilecn.chat{

   

    import flash.display.Sprite;

   

    import flash.net.NetConnection;

   

    import flash.events.NetStatusEvent;

   

    public class Chat extends Sprite{

       

        private var nc:NetConnection;

        private var rtmpUrl:String = "rtmp://localhost/chat";

   

        public function Chat():void{

            nc=new NetConnection();

            nc.addEventListener (NetStatusEvent.NET_STATUS,netStatusHandler);

            nc.connect (rtmpUrl);

        }

       

        private function netStatusHandler(event:NetStatusEvent):void{

            trace(event.info.code);

        }

    }

   

}

这段代码里我们导入了一个NetConnection,这个类是FLASH里用于跟网络连接相关的操作,像我们连接FMS,remoting(我前面的一步一步学ActionScript 3[十六]里面有相关介绍).

 

NetStatusEvent是一个检测状态的事件

 

rtmpUrl是一个连接FMS的字符串,rtmpFMS用的一个网络协议,localhost是服务器的IP,这里我们是本机,所以是 localhost,如果放在网上,应该是你网上的IP,chat是应用程序名,就是之前我们建立的chat目录.这里我们完整的地址就 :rtmp://localhost/chat,其实如果是localhost,地址可以这样写:rtmpe:/localhost.

 

这个程序动行后会traceNetConnection.Connect.Success,这个信息表示的是我们连接FMS成功了.

 

这是一个连接状态,event.info.code就是这个状态,相关的状态还有:

 

NetConnection.Connect.Closed 

分享到:
评论
4 楼 qichunren 2008-12-15  
很好的文章。/
3 楼 hereson 2008-08-26  
在上一节中,我们学会了在FMS中使用类,虽然不是正式意义上的类,但也会使我们的程序看起来更结构化,这一节我们继续用类的方式改造之前的代码,首先我们要加个用户类(User.asc):


function User(client,userName){
    this.client = client;
    this.userName = this.client.userName = userName;
}
很简单的一个类,只有构造,没有方法,其实是当用户的信息更多时,我们通常会将用户的所有信息都封装起来,这样便于我们使用,这里我们只是比前面多加了一个client对象,这个对象是代表连接进来的客户端,我们把它封装到User里面.

接着,UserList.asc也要做相应该的修改,并且把之前在main.asc里面的sendUserList和sendMsgToClient也封装到UserList这个类中.

function UserList(){
    this.listArray = [];//也可以用new Array(),不过听说[]效率更高;
}
 
UserList.prototype.addUser = function(user){
    this.listArray.push(user);
    this.sendUserList();
}
 
UserList.prototype.delUser = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i].userName == userName){
            this.listArray.splice(i,1);
            break;
        }
    }
    this.sendUserList();
}
 
UserList.prototype.checkOnline = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i].userName == userName){
            return true;
        }
    }
    return false;
}
 
UserList.prototype.getUserList = function(){
    var result = [];
    var len = this.listArray.length;
    for(var i= 0;i<len;i++){
        result.push(this.listArray[i].userName);
    }
    return result;
}
 
UserList.prototype.sendUserList = function(){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        this.listArray[i].client.call("getUserList",null,this.getUserList());
    }
}
 
UserList.prototype.sendMsgToClient = function(chatInfo){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        this.listArray[i].client.call("getMsgInfo",null,chatInfo);
    }
}
在这个类中我们主要做了以下修改:

1.在addUser里传进来的参数是一个User对象,我们的列表里存的是每个人的对象,而不是以前的只一个userName了.
2.在delUser里,我们做比较的是列表里对象的userName和传进来的参数
3.getUserList方法中我们要把每个人的名字筛选出来
4.将sendUserList和sendMsgToClient封装进来,因为我们的用户对象中有Client,所以直接用this.listArray[i].client就可以调用客户端方法了.
5.在addUser和delUser后通过this.sendUserList()来发送在线列表.

通过将一些方法封装进来,我们发现main.asc的代码减少了,这是好处之一,还有一个好处在后面的教程中会讲到.

最后来看一下main.asc文件:

load("UserList.asc");
load("User.asc");
application.onAppStart = function() {
    this.chatMsgArray = new Array();
    this.userList = new UserList();
}
 
application.onConnect = function(client, userName) {
    if(this.userList.checkOnline(userName)){
        this.rejectConnection(client,{msg:"error1"});
        return;
    }
    this.acceptConnection(client);
    var user = new User(client,userName);
    this.userList.addUser(user);
    //客户端调用方法
    client.getMsg = function(){
        return application.chatMsgArray;
    }
    client.sendMsg = function(msg){
        var chatInfo = this.userName + " : " + msg;
        application.chatMsgArray.push(chatInfo);
        application.userList.sendMsgToClient(chatInfo);
    }
}
 
application.onDisconnect = function(client) {
    trace("用户:"+client.userName+" 离开");
    this.userList.delUser(client.userName);
}
 
 
application.onAppStop = function() {
   
}
在main.asc中减少了两个方法,并且多了一个load(”User.asc”)将User.asc载入进来

在接受用户后,新建了一个User对象 — var user = new User(client,userName);

还有一个改动就是:this.rejectConnection(client,{msg:”error1″});

我们在用户已经列表中拒绝用户加了一个参数,当你有不同的拒绝信息时,这个参数会起很大的作用

客户端代码也要做相应的修改:

package net.smilecn.chat{
   
    import flash.display.Sprite;
    import flash.net.NetConnection;
    import flash.net.Responder;
    import flash.events.NetStatusEvent;
    import flash.events.SyncEvent;
    import flash.events.MouseEvent;
    import fl.controls.TextArea;
      import fl.controls.Button;
      import fl.controls.TextInput;
    import fl.controls.Label;
    import fl.controls.List;
    import fl.data.DataProvider;
 
   
    public class Chat extends Sprite{
       
        private var nc:NetConnection;
        private var rtmpUrl:String = "rtmp://localhost/chat";
        private var msg:Label;
        private var userNameInput:TextInput;
        private var enterBtn:Button;
        private var button:Button;
        private var textArea:TextArea;
        private var textInput:TextInput;
        private var userList:List;
       
        private var userName:String;
   
        public function Chat():void{
            userNameInput = new TextInput();
            userNameInput.move(100,200);
            addChild(userNameInput);
            enterBtn = new Button();
            enterBtn.move(220,200);
            enterBtn.label = "进入";
            addChild(enterBtn);
            enterBtn.addEventListener(MouseEvent.CLICK,enterBtnClickHandler);
            msg = new Label();
            msg.move(100,230);
            msg.text = "";
            addChild(msg);
        }
       
        private function enterBtnClickHandler(event:MouseEvent):void{
            if(userNameInput.text!=""){
                userName = userNameInput.text;
                nc=new NetConnection();
                        nc.addEventListener (NetStatusEvent.NET_STATUS,netStatusHandler);
                nc.client = this;
                nc.connect (rtmpUrl,userName);
               
            }else{
                msg.text = "请输入用户名";
            }
           
        }
       
        private function into():void{
            removeChild(userNameInput);
            removeChild(enterBtn);
            removeChild(msg);
           
            textArea=new TextArea();
            textArea.setSize (200,300);
            textArea.move (20,20);
            addChild (textArea);
 
            textInput=new TextInput();
            textInput.width = 140;
            textInput.move (20,330);
            addChild (textInput);
 
                   button=new Button();
            button.width=50;
                   button.label="发送";
                   button.move (170,330);
            addChild(button);
            button.addEventListener (MouseEvent.CLICK,sendMsg);
               
            userList = new List();
            userList.move(250,20);
            userList.setSize(100,300);
            addChild(userList);
        }
       
        private function netStatusHandler(event:NetStatusEvent):void{
            trace(event.info.code);
            switch (event.info.code) {
                case "NetConnection.Connect.Success" :
                    trace("连接成功!");
                    into();
                    nc.call("getMsg",new Responder(getMsgResult,getMsgFault))
                    break;
                case "NetConnection.Connect.Rejected" :
                    trace("连接被拒绝!:"+event.info.application.msg);
                    if(event.info.application.msg == "error1"){
                        msg.text = "用户已在列表中";
                    }
                    break;
                case "NetConnection.Connect.Failed" :
                    trace("连接失败!");
                    break;
                case "NetConnection.Connect.Closed" :
                    trace("连接关闭!");
                    break;
            }
        }
       
        private function getMsgResult(re:Array):void{
            var s:String="";
            var len:Number = re.length;
            for(var i=0;i<len;i++){
                s += re[i]+"\n";
            }
            textArea.text = s;
        }
       
        private function getMsgFault(fe:*):void{
            trace(fe);
        }
       
        private function sendMsg (e:MouseEvent):void{
            nc.call("sendMsg",null,textInput.text);
            textInput.text = "";
        }
       
        public function getMsgInfo(msg:String):void{
            textArea.appendText(msg+"\n");
        }
       
        public function getUserList(list:Array):void{
            userList.dataProvider = new DataProvider(list);
        }
       
        public function close():void{
           
        }
    }
   
}
代码比较简单,不用多讲解了,发布看一下效果,效果跟前几节还是一样,只是多了一个”用户已在列表中的提示”

在上一节中,我们将一些方法进一步封装到了UserList类中 了,这样做是有很多好处的,以前经常有朋友问我像聊天室怎么做成多房间,像斗地主怎么做成多个桌子在打,其实原理都是一样的,我们只需要对 UserList类做一些改造,就可以达到这个功能了,首先把类的名字改成ChatRoom,这样更好认一些,先看ChatRoom.asc,是由 UserList.asc改造而来的:


function ChatRoom(id){
    this.id = id;
    this.listArray = [];
    this.chatMsgArray = [];
}
 
ChatRoom.prototype.addUser = function(user){
    this.listArray.push(user);
    this.sendUserList();
}
 
ChatRoom.prototype.delUser = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i].userName == userName){
            this.listArray.splice(i,1);
            break;
        }
    }
    this.sendUserList();
}
 
ChatRoom.prototype.checkOnline = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i].userName == userName){
            return true;
        }
    }
    return false;
}
 
ChatRoom.prototype.getUserList = function(){
    var result = [];
    var len = this.listArray.length;
    for(var i= 0;i<len;i++){
        result.push(this.listArray[i].userName);
    }
    return result;
}
 
ChatRoom.prototype.sendUserList = function(){
    sendAllRoomNum(this.id,this.listArray.length);
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        this.listArray[i].client.call("getUserList",null,this.getUserList());
    }
}
 
ChatRoom.prototype.sendMsgToClient = function(chatInfo){
    trace("sendMsgToClient:"+chatInfo);
    this.chatMsgArray.push(chatInfo);
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        this.listArray[i].client.call("getMsgInfo",null,chatInfo);
    }
}
来看看ChatRoom类和UserList类有什么区别:
1.在构造中增加了this.id = id,这里的id表示每个房间的编号,这样可以区别当前是哪个房间.
2.this.chatMsgArray,存储聊天信息的数组,之前是放在main.asc里面的,因为之前只有一个房间,而现在有多个房间,所以每个房间都应该有一个自己的聊天信息列表.

main.asc文件也做了相应的修改:

load("ChatRoom.asc");
load("User.asc");
application.onAppStart = function() {
    this.roomArray = [];
    this.roomUserNum = [];
    var roomLen = 3;
    for(var i=0;i< roomLen ;i++){
        this.roomArray.push(new ChatRoom(i+1));
        this.roomUserNum.push(0);
    }
}
 
application.onConnect = function(client) {
    this.acceptConnection(client);
    //客户端调用方法
    client.addRoom = function(userName,roomid){
        trace(userName,roomid);
        var isOnline = application.roomArray[roomid - 1].checkOnline(userName);
        if(!isOnline){
            var user = new User(this,userName,roomid);
            application.roomArray[roomid - 1].addUser(user);
            return true;
        }else{
            return false;
        }
    }
    client.getMsg = function(roomid){
        return application.roomArray[roomid - 1].chatMsgArray;
    }
    client.sendMsg = function(roomid,msg){
        var chatInfo = this.userName + " : " + msg;
        application.roomArray[roomid - 1].sendMsgToClient(chatInfo);
    }
    client.getAllUserNum = function(){
        return application.roomUserNum;
    }
}
 
application.onDisconnect = function(client) {
    trace("用户:"+client.userName+" 离开  "+client.roomid);
    this.roomArray[client.roomid - 1].delUser(client.userName);
}
 
 
application.onAppStop = function() {
   
}
 
function sendAllRoomNum(roomid,num){
    application.roomUserNum[roomid - 1] = num;
    var len = application.clients.length;
    for(var i= 0;i<len ;i++){
        application.clients[i].call("getAllRoomNum",null,application.roomUserNum);
    }
}
在这个类中我们主要做了以下修改:

1.this.roomArray,用来存储每个房间实例的数组;this.roomUserNum,所有房间当前在线人数的数组;在这里我定义了三个房间,这个可以任意设定.
2.在客户端连接进来时,直接接受客户端连接,不做判断,把判断改在addRoom完成.
3.在客户端发过来的消息中,多加了个参数,就是roomid,用于区别是哪个房间发来的消息,然后根据roomid来判断调用哪个房间实例的方法.
4.增加sendAllRoomNum方法,用于发送实时的所有房间人数,供客户端的大厅显示,正常大厅的可以实时看到人数变化.

客户端代码也要做相应的修改:

package net.smilecn.chat{
   
    import flash.display.Sprite;
    import flash.net.NetConnection;
    import flash.net.Responder;
    import flash.events.NetStatusEvent;
    import flash.events.SyncEvent;
    import flash.events.MouseEvent;
    import fl.controls.TextArea;
      import fl.controls.Button;
      import fl.controls.TextInput;
    import fl.controls.Label;
    import fl.controls.List;
    import fl.data.DataProvider;
 
   
    public class Chat extends Sprite{
       
        private var nc:NetConnection;
        private var rtmpUrl:String = "rtmp://localhost/chat/chatLobby";
        private var msg:Label;
        private var userNameInput:TextInput;
        private var enterBtn:Button;
        private var button:Button;
        private var textArea:TextArea;
            private var textInput:TextInput;
        private var userList:List;
       
        private var userName:String;
       
        private var allRoomNum :Array = new Array();
        private var roomBtnArray:Array = new Array();
       
        private var onlineArray:Array = new Array();
       
        private var currentRoomid:Number;
   
        public function Chat():void{
            userNameInput = new TextInput();
            userNameInput.move(100,200);
            addChild(userNameInput);
            enterBtn = new Button();
            enterBtn.move(220,200);
            enterBtn.label = "进入";
            addChild(enterBtn);
            enterBtn.addEventListener(MouseEvent.CLICK,enterBtnClickHandler);
            msg = new Label();
            msg.move(100,230);
            msg.text = "";
            addChild(msg);
        }
       
        private function enterBtnClickHandler(event:MouseEvent):void{
            if(userNameInput.text!=""){
                userName = userNameInput.text;
                nc=new NetConnection();
                        nc.addEventListener (NetStatusEvent.NET_STATUS,netStatusHandler);
                nc.client = this;
                nc.connect (rtmpUrl);
               
            }else{
                msg.text = "请输入用户名";
            }
           
        }
       
        private function netStatusHandler(event:NetStatusEvent):void{
            trace(event.info.code);
            switch (event.info.code) {
                case "NetConnection.Connect.Success" :
                    trace("连接成功!");
                    nc.call("getAllUserNum",new Responder(getAllUserNumResult,getAllUserNumFault))
                    break;
                case "NetConnection.Connect.Rejected" :
                    trace("连接被拒绝!");
                    break;
                case "NetConnection.Connect.Failed" :
                    trace("连接失败!");
                    break;
                case "NetConnection.Connect.Closed" :
                    trace("连接关闭!");
                    break;
            }
        }
       
        private function getAllUserNumResult(re:Array):void{
            trace("getAllUserNumResult:"+re);
            removeChild(userNameInput);
            removeChild(enterBtn);
            removeChild(msg);
            allRoomNum = re;
            var xpos = 100;
            var ypos = 200;
            var num = 150;
            var len = allRoomNum.length;
            for(var i=0 ;i<len;i++){
                var btn:Button = new Button();
                btn.x = xpos;
                btn.y = ypos;
                xpos += num;
                btn.label = (i+1) + "号房间("+allRoomNum[i]+"/"+(i+1) * 10 + ")";
                addChild(btn);
                btn.addEventListener(MouseEvent.CLICK,roomBtnHandler);
                roomBtnArray.push(btn);
            }
        }
       
       
        private function getAllUserNumFault(fe:*):void{
            trace(fe);
        }
       
        private function intoRoom():void{
            var len = roomBtnArray.length;
            for(var i=0;i<len;i++){
                removeChild(roomBtnArray[i]);
            }
            roomBtnArray = [];
            textArea=new TextArea();
            textArea.setSize (200,300);
                   textArea.move (20,20);
                    addChild (textArea);
 
                    textInput=new TextInput();
            textInput.width = 140;
                    textInput.move (20,330);
                    addChild (textInput);
 
                   button=new Button();
                    button.width=50;
                   button.label="发送";
                   button.move (170,330);
            addChild(button);
            button.addEventListener (MouseEvent.CLICK,sendMsg);
               
            userList = new List();
            userList.move(250,20);
            userList.setSize(100,300);
            addChild(userList);
            userList.dataProvider = new DataProvider(onlineArray);
            nc.call("getMsg",new Responder(getMsgResult,getMsgFault),currentRoomid);
        }
       
        private function getMsgResult(re:*):void{
            var msg:String = "";
            var len:Number = re.length;
            for(var i=0;i<len;i++){
                msg += re[i]+"\n";
            }
            textArea.text = msg;
        }
       
        private function getMsgFault(fe:*):void{
           
        }
       
        private function roomBtnHandler(event:MouseEvent):void{
            var roomid = Number(event.currentTarget.label.substr(0,1));
            if(allRoomNum[roomid - 1] == roomid * 10){
                event.currentTarget.label = "此房间已满";
            }else{
                nc.call("addRoom",new Responder(addRoomResult,addRoomFault),userName,roomid);
                currentRoomid = roomid;
            }
        }
       
        private function addRoomResult(re:*):void{
            trace(re);
            if(re){
                intoRoom();
            }
        }
       
        private function addRoomFault(fe:*):void{
           
        }
       
        private function sendMsg (e:MouseEvent):void{
            nc.call("sendMsg",null,currentRoomid,textInput.text);
            textInput.text = "";
        }
       
        public function getMsgInfo(msg:String):void{
            textArea.appendText(msg+"\n");
        }
       
        public function getUserList(list:Array):void{
            trace("getuserList:"+list);
            onlineArray = list;
            if(userList){
                userList.dataProvider = new DataProvider(onlineArray);
            }
        }
       
        public function getAllRoomNum(array:Array):void{
            allRoomNum = array;
            var len = roomBtnArray.length;
            if(len > 0){
                for(var i=0;i<len;i++){
                    roomBtnArray[i].label = (i+1) + "号房间("+allRoomNum[i]+"/"+(i+1) * 10 + ")";
                }
            }
        }
       
        public function close():void{
           
        }
    }
   
}
客户端增加一个大厅,然后再进房间,客户端的代码我就不详细讲解了,因为这不是一个讲AS3的教程,会一些AS3的朋友应该都看得懂,如果不懂,可 以先看一下我写的关于AS3的教程,这个客户端代码写得有点乱,只是完成了功能,FMS的教程写完后,我准备写一些关了MVC框架的使用教程,像 cairngorm和pureMVC,到时会将代码处理得更好些.

下节继续.
2 楼 hereson 2008-08-26  
当我们要加的功能越来越多时,就会发现程序会越写越大,这样我们就需要更好的组织我们的程序,用类是最好方法,但FMS用的是AS1.0的语法,没有真正意义的类,但也能完成类的简

单功能,不管怎么样,总比没有类好,今天我们就来看一下如果在AS1.0里使用类,当然这不是真正的类.

现在我们将上一节中用到的用户列表做一下修改,写成一个类,上一节中我们用了userListArray这样一个数组来存储用户列表,现在我们把用户列表写成一个类:


先建一个UserList.asc文件,这就是我们要用的类的文件名,当然UserList也是类名(其实并不需要文件名和类名相同,因为这不是真正意义上的类)

function UserList(){
    this.listArray = [];//也可以用new Array(),不过听说[]效率更高;
}
 
UserList.prototype.addUser = function(userName){
    this.listArray.push(userName);
}
 
UserList.prototype.delUser = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i] == userName){
            this.listArray.splice(i,1);
            break;
        }
    }
}
 
UserList.prototype.checkOnline = function(userName){
    var len = this.listArray.length;
    for(var i=0;i<len;i++){
        if(this.listArray[i] == userName){
            return true;
        }
    }
    return false;
}
 
UserList.prototype.getUserList = function(){
    return this.listArray;
}
function function UserList(){} 这个相当于类里面的构造函数

UserList.prototype. 这种相当于给UserList这个类加入方法,用过as1.0或者as2.0的朋友应该都知道prototype的用法

在这里我们加了四个方法

addUser 将用户加入到列表中
delUser 将用户从列表中删除
checkOnline 检查用户是否在列表中
getUserList 得到用户列表数组

这些代码应该很容易懂

再来看修改后的main.asc:

load("UserList.asc");
application.onAppStart = function() {
    this.chatMsgArray = new Array();
    this.userList = new UserList();
}
 
application.onConnect = function(client, userName) {
    if(this.userList.checkOnline(userName)){
        this.rejectConnection(client);
        return;
    }
    this.acceptConnection(client);
    client.userName = userName;
    this.userList.addUser(userName);
    sendUserList();
    //客户端调用方法
    client.getMsg = function(){
        return application.chatMsgArray;
    }
    client.sendMsg = function(msg){
        var chatInfo = this.userName + " : " + msg;
        application.chatMsgArray.push(chatInfo);
        sendMsgToClient(chatInfo);
    }
}
 
application.onDisconnect = function(client) {
    trace("用户:"+client.userName+" 离开");
    this.userList.delUser(client.userName);
    sendUserList();
}
 
 
application.onAppStop = function() {
   
}
 
function sendMsgToClient(chatInfo){
    var len = application.clients.length;
    for(var i=0;i<len;i++){
        application.clients[i].call("getMsgInfo",null,chatInfo);
    }
}
 
function sendUserList(){
    var len = application.clients.length;
    for(var i=0;i<len;i++){
        application.clients[i].call("getUserList",null,application.userList.getUserList());
    }
}
跟上一节的代码相比,首先多了一个load(”UserList.asc”),load能够将其它的asc文件加入进来,相当于导入了,也可以理解为包括,就是两个文件成了一个文件

其他的修改应该很简单,很容易看懂,我就不多讲了.

从这个程序代码的多少上看,并没有减少多少,但这只是一个小程序,当程序越大时就能看出这样写的好处了.

客户端不用改,现在看一下效果,应该和上一节是一样的.

下节继续.

1 楼 hereson 2008-08-26  
在连接成功之后,有这样一句代码:chatMsg_so=SharedObject.getRemote(”chatMsg”,nc.uri,false);

chatMsg_so是我们定义的一个SharedObject实例,SharedObject–共享对象,官方的解释是这样的:

SharedObject 类用于在用户计算机或服务器上读取和存储有限的数据量。 使用共享对象,可在永久贮存在本地计算机或远程服务器上的多个客户端 SWF 文件和对象之间实现实时数据共享。 本地共享对象类似于浏览器 Cookie,远程共享对象类似于实时数据传输设备。

SharedObject可以用来存储数据,这里我们用到它是存储我们的聊天数据.

getRemote方法是得到一个远程共享对象,就是FMS上的一个共享对象,名字叫chatMsg,地址是nc.uri,最后一个参数是是否以文 件的形式保存下来,false表示不保存,当服务器上有这样一个名为chatMsg的共享对象时,这个方法就会得到这个共享对象,如果没有这相共享对象, 就会创建一个名为chatMsg的共享对象.

我们的代码中,第一个客户连进去后会创建一个共享对象,其它用户再进去,就是得到这个共享对象.

chatMsg_so.addEventListener (SyncEvent.SYNC,checkSO);

这句代码是侦听共享对象的状态

event.changeList[i].code就是状态,这里我们用了三个状态—-clear,success,change

clear是清除数据时(我们的代码中第一个人进去时会触发)
success是成功(我们的代码中自己发消息时会触发)
change是改变(我们的代码中别人发消息时会触发)

我们实现这个聊天功能实际是每个人去改变这个共享对象,然后共享对象改变了就会通知所有的客户端,其它人就会收到这个消息(注意自己不会收到change消息).

这个聊天在实际中用处不大,这里只是介绍共享对象的一个使用方式,而且这种聊天还有很多功能都没有实现.(本人不喜欢用共享对象)

在后面我们会讲解怎么写服务端代码,这两节都没有写服务端代码.下节继续.

今天我们的讲解的是在昨天代码的功能上加上在线列表的功能,同时会去掉共享对象,用另一种方法向客户端发消息.

先看看服务端代码我们更改些什么代码:


application.onAppStart = function() {
    this.chatMsgArray = new Array();
    this.userListArray = new Array();
}
 
application.onConnect = function(client, userName) {
    if(checkOnline(userName)){
        this.rejectConnection(client);
        return;
    }
    this.acceptConnection(client);
    client.userName = userName;
    this.userListArray.push(userName);
    sendUserList();
    //客户端调用方法
    client.getMsg = function(){
        return application.chatMsgArray;
    }
    client.sendMsg = function(msg){
        var chatInfo = this.userName + " : " + msg;
        application.chatMsgArray.push(chatInfo);
        sendMsgToClient(chatInfo);
    }
}
 
application.onDisconnect = function(client) {
    trace("用户:"+client.userName+" 离开");
    var len = this.userListArray.length;
    for(var i=0;i<len;i++){
        if(this.userListArray[i] == client.userName){
            this.userListArray.splice(i,1);
            sendUserList();
        }
    }
}
 
 
application.onAppStop = function() {
    delete this.chatMsg_so;
}
 
function checkOnline(userName){
    var len = application.userListArray.length;
    for(var i=0;i<len;i++){
        if(application.userListArray[i] == userName){
            return true;
        }
    }
    return false;
}
 
function sendMsgToClient(chatInfo){
    var len = application.clients.length;
    for(var i=0;i<len;i++){
        application.clients[i].call("getMsgInfo",null,chatInfo);
    }
}
 
function sendUserList(){
    var len = application.clients.length;
    for(var i=0;i<len;i++){
        application.clients[i].call("getUserList",null,application.userListArray);
    }
}
this.chatMsgArray = new Array();
this.userListArray = new Array();

这两个定义的数组是在开始定义的,chatMsgArray是存储所有的聊天信息,userListArray是一个存储在线列表的数组

当用户连接进来的时候,我们用了一个函数checkOnline来检查用户是不是在在线列表中,如果在就用this.rejectConnection(client);拒绝这个连接,如果不在就接受这个连接并加到在线列表中

sendUserList函数是向所以客户端发送在线列表信息,application.clients是一个存储所以客户端连接的信息,application.clients.call就是调用客户端函数,使用方法跟客户端调服务器端是一样的.

然后我们增加了1个供客户端调用的函数

client.getMsg = function(){
      return application.chatMsgArray;
}
client.getMsg是返回所有的聊天信息,当用户第一次连接时,得到别人的聊天记录

在用户断开连接的时候增加了将断线用户从在线列表中清除,并且发送在线列表.

再来看看客户端代码:

package net.smilecn.chat{
   
    import flash.display.Sprite;
    import flash.net.NetConnection;
    import flash.net.Responder;
    import flash.events.NetStatusEvent;
    import flash.events.SyncEvent;
    import flash.events.MouseEvent;
    import fl.controls.TextArea;
    import fl.controls.Button;
    import fl.controls.TextInput;
    import fl.controls.Label;
    import fl.controls.List;
    import fl.data.DataProvider;
 
   
    public class Chat extends Sprite{
       
        private var nc:NetConnection;
        private var rtmpUrl:String = "rtmp://localhost/chat";
        private var msg:Label;
        private var userNameInput:TextInput;
        private var enterBtn:Button;
        private var button:Button;
        private var textArea:TextArea;
        private var textInput:TextInput;
        private var userList:List;
       
        private var userName:String = "user002";
   
        public function Chat():void{
            userNameInput = new TextInput();
            userNameInput.move(100,200);
            addChild(userNameInput);
            enterBtn = new Button();
            enterBtn.move(220,200);
            enterBtn.label = "进入";
            addChild(enterBtn);
            enterBtn.addEventListener(MouseEvent.CLICK,enterBtnClickHandler);
            msg = new Label();
            msg.move(100,230);
            msg.text = "";
            addChild(msg);
        }
       
        private function enterBtnClickHandler(event:MouseEvent):void{
            if(userNameInput.text!=""){
                userName = userNameInput.text;
                removeChild(userNameInput);
                removeChild(enterBtn);
                removeChild(msg);
           
                textArea=new TextArea();
                textArea.setSize (200,300);
                textArea.move (20,20);
                addChild (textArea);
 
                textInput=new TextInput();
                textInput.width = 140;
                textInput.move (20,330);
                addChild (textInput);
 
                button=new Button();
                button.width=50;
                button.label="发送";
                button.move (170,330);
                addChild(button);
                button.addEventListener (MouseEvent.CLICK,sendMsg);
               
                userList = new List();
                userList.move(250,20);
                userList.setSize(100,300);
                addChild(userList);
           
                nc=new NetConnection();
                nc.addEventListener (NetStatusEvent.NET_STATUS,netStatusHandler);
                nc.connect (rtmpUrl,userName);
                nc.client = this;
            }else{
                msg.text = "请输入用户名";
            }
           
        }
       
        private function netStatusHandler(event:NetStatusEvent):void{
            trace("event.info.code:",event.info.code);
            if(event.info.code == "NetConnection.Connect.Success"){
                nc.call("getMsg",new Responder(getMsgResult,getMsgFault))
            }
        }
       
        private function getMsgResult(re:Array):void{
            var s:String="";
            var len:Number = re.length;
            for(var i=0;i<len;i++){
                s += re[i]+" ";
            }
            textArea.text = s;
        }
       
        private function getMsgFault(fe:*):void{
            trace(fe);
        }
       
        private function sendMsg (e:MouseEvent):void{
            nc.call("sendMsg",null,textInput.text);
            textInput.text = "";
        }
       
        public function getMsgInfo(msg:String):void{
            textArea.appendText(msg+" ");
        }
       
        public function getUserList(list:Array):void{
            userList.dataProvider = new DataProvider(list);
        }
    }
   
}
客户端首先增加了一个用户输入名字,这是些简单的代码.

然后共享对象的那部份去掉了,加了句这样的代码:nc.client = this;

这句话的意思是指定当前对象为服务器回调方法的对象

public function getMsgInfo(msg:String):void{
       textArea.appendText(msg+"\n");
}
 
public function getUserList(list:Array):void{
       userList.dataProvider = new DataProvider(list);
}
这两个方法就是服务器调用的方法.

在nc连接成功的地方加了句nc.call(”getMsg”,new Responder(getMsgResult,getMsgFault)),这句是在刚连接成功的时候得到以前用户的聊天记录.

顺便说一句,客户端代码用到了组件,所以要把这些组件放到库中才能运行,上一节也是这样的.

下节继续.

相关推荐

    Flash_Media_Server-config.rar_flash media server

    **Flash Media Server (FMS) 是一款由Adobe公司开发的流媒体服务器软件,主要用于实时音频、视频的发布、录制和互动。本资料旨在提供Flash Media Server的配置与开发基础,帮助初学者快速入门。** ## 一、Flash ...

    Flash Media Server 4文档

    《Flash Media Server 4.5 安装指南详解》 Flash Media Server 4.5 (FMS 4.5) 是Adobe公司推出的一款强大的流媒体服务器软件,它为开发人员和内容创作者提供了丰富的实时交互性视频和音频服务。在安装FMS 4.5之前,...

    FMS3.5使用教程

    【Flash Media Server 3.5 使用教程】 Flash Media Server (FMS) 3.5 是 Adobe 公司推出的一款强大的多媒体服务器,专为处理Flash应用程序和流媒体内容设计。本教程将逐步指导初学者如何安装和使用FMS 3.5。 首先...

    使用Red5_FFMpeg搭建在线Flash流媒体分享平台

    Red5是一款基于Java的开源Flash流媒体服务器,其设计初衷在于替代Adobe的商用产品Flash Media Server(FMS)。Red5采用RTSP(Real-Time Streaming Protocol)作为流媒体传输协议,这使得它能够提供稳定且高效的实时...

    网页版视频聊天室 视频聊天程序 Feelink V2.5

    新版本的Feelink不仅优化了原有的性能,还增加了P2P(点对点)和FMS(Flash Media Server)智能双模式,确保在保证视频流畅度的同时,降低了运营成本,更好地满足了用户多样化的需求。 视频聊天室的核心技术主要...

    libCRDevice.rar

    FFmpeg的RTMP(Real Time Messaging Protocol)推流器允许将音视频流推送到支持RTMP协议的服务器,如YouTube Live或Adobe Flash Media Server。然而,在推流之前,系统需要知道有哪些可用的视频输入设备,比如内置...

    Kindeditor编辑器Demo

    &lt;add key="Attach_Media" value="swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb"/&gt; ,docx,xls,xlsx,ppt,pptx,pdf,htm,html,txt,js,zip,rar,gz,7z,bz2"/&gt; 2.&lt;system.web&gt;............节点中加入以下代码 &lt;!...

    读取rtsp摄像头AI分析后推流到RTMP服务器

    RTMP服务器如Nginx-RTMP模块或Adobe Flash Media Server接收这些流,并将其分发给观看者或存储供后续回放。 以下是一般流程: 1. 使用OpenCV从RTSP源读取视频流。 2. 将视频帧解码,通过预训练的AI模型进行分析。 ...

    车站oracle客户端安装及配置1

    在`s01/rpm`目录下,逐个执行`yum -y install`命令来安装所需的Oracle相关RPM包,包括`compat-libstdc++-33`,`kernel-uek-firmware`,`kernel-uek`,`oracle-rdbms-server-12cR1-preinstall`以及`flash-player-...

    citrix XenApp5 For win2003

    2. **组件选择**:安装向导中,可选择性勾选HDX Media Stream for Flash组件。接着,勾选Citrix Licensing组件,并继续下一步。 3. **创建Farm(集群)**:设定Farm Name,选择默认的Access数据库作为DataStore。在...

    Fckeditor2.6.3

    这是我改过的Basic,把图像功能去掉,把添加链接功能去掉,因为图像和链接和flash和图像按钮添加功能都能让前台页直接访问和上传文件,要是这儿不改直接给你上传个木马还不马上玩完? 当然也可以配置一下WebConfig,...

Global site tag (gtag.js) - Google Analytics