先来看客户端fla的构成:
第一帧:登录界面
第一帧的代码:
import flash.events.MouseEvent; import com.adobe.utils.StringUtil; import utils.Alert; stop(); var userName:String=""; Alert.init(stage); btnLogin.addEventListener(MouseEvent.CLICK,btnLoginClick); function btnLoginClick(e:MouseEvent):void{ txtUserName.text = StringUtil.trim(txtUserName.text); this.userName = txtUserName.text; if (this.userName.length<=0){ Alert.show("请输入用户名!",null,0xefefef,0x000000); } else{ gotoAndPlay(2); } }
第二帧:聊天的主界面
代码:
import flash.net.NetConnection; import flash.events.MouseEvent; import flash.events.NetStatusEvent; import flash.events.KeyboardEvent; import utils.Alert; import flash.ui.Keyboard; stop(); var nc:NetConnection; function init():void { nc = new NetConnection(); nc.client=new Object(); //定义供服务端广播消息时需要的客户端函数(结合main.asc查看) nc.client.showmsg = function (str:String):void { txtMsg.appendText(str + "\n"); }; nc.addEventListener(NetStatusEvent.NET_STATUS,connHandler); nc.connect("rtmp://localhost/chat",userName);//注:这里的userName是在第一帧定义的 updateMsg("正在连接服务器..."); btnSend.addEventListener(MouseEvent.CLICK,btnSendClick); txtSend.addEventListener(KeyboardEvent.KEY_DOWN,txtSendKeyDown); } function txtSendKeyDown(e:KeyboardEvent):void { if (e.keyCode == Keyboard.ENTER) { btnSendClick(null); } } function btnSendClick(e:MouseEvent):void { txtSend.text = StringUtil.trim(txtSend.text); if (txtSend.text != "") { nc.call("sendmsg",null,txtSend.text); txtSend.text = ""; } else { Alert.show("请输入要发送的内容!",null,0xefefef,0x000000); } } function connHandler(e:NetStatusEvent) { switch (e.info.code) { case "NetConnection.Connect.Closed" : updateMsg("成功关闭连接!"); break; case "NetConnection.Connect.Failed" : updateMsg("连接尝试失败!"); break; case "NetConnection.Connect.Success" : updateMsg("服务器连接成功!"); break; case "NetConnection.Connect.Rejected" : updateMsg("连接尝试没有访问应用程序的权限!"); break; default : txtMsg.appendText(e.info.code + "\n"); break; } } function updateMsg(msg:String):void { txtMsg.appendText(msg + "\n"); } init();
服务端main.asc的处理(注:main.asc保存时,貌似只能选择为utf-8编码,否则运动时客户端一直连接不上)
application.onAppStart = function() { trace("onAppStart"); }; //新客户端连接时触发 application.onConnect = function(client, uName) { trace("onConnect = "+uName); client.UserName = uName; application.acceptConnection(client);//允许客户登录,如果要对客户身份做验证,在此扩展即可 hellomsg="系统信息:"+client.UserName+" 进入聊天室"; application.broadcastMsg("showmsg",hellomsg);//调用所有client的showmsg方法,并传递参数hellomsg(客户端的代码中,必须有对应的showmsg函数) //定义服务端的sendmsg方法,以便客户端能调用 client.sendmsg = function(msg) { mesg = client.UserName+": "+msg; //每次client调用本方法后,服务器同步广播到所有client application.broadcastMsg("showmsg",mesg) }; }; //有客户端断开连接时触发 application.onDisconnect = function(client) { trace("onDisconnect ="+client.UserName); hellomsg="系统信息:"+client.UserName+" 离开聊天室"; application.broadcastMsg("showmsg",hellomsg) }; application.onAppStop = function() { trace("onAppStop"); };
运行中的样子:
注:艾睿论坛上曾有一篇教程利用FMS的远程共享对象来创建聊天室,远程对象在编码上也许更简单,不过个人感觉性能不太理想(因为对于共享对象的广播是FMS自动的,完全不受控制,不管客户端想不想接收消息,都会被动接收消息),本文演示的是另一种常见做法(服务端可以有选择性的仅向某些Client发送消息),而且网上也有很多相关文章,只不过要么是收费的,要么是基于AS2.0的,今天用AS3.0整理出来,于已方便、与人方便。
源代码下载:http://cid-2959920b8267aaca.skydrive.live.com/self.aspx/Flash/FMSTxtChatBasicDemo.rar
后话:这个跟silverlight做的聊天室(基于scoket或wcf双工通讯)有什么不同呢? 答:silverlight做聊天室应用,往往需要开发者自己做一个(scoket) server端,而adobe的fms正好充当了这个角色,省去了这一步之后,程序员只需要把注意力集中在客户端和业务逻辑上即可,开发量大大缩减。