FMS流媒体,貌似都销声匿迹了。不过,既然曾经做过。现在就温习回复一遍吧。
FMS是一种C/S模式,通过FMS流媒体服务器,可以实现客户端的和客户端的通信,客户端间视频流,音频流的共享。
对于FMS,第一个API就是NetConnection,它建立客户与服务端的连接。为了展示效果。我们模拟一个多个客户端,点击登录的场景。然后,可以进行群聊天或一对一私聊。不过,为了简单测试,这里没有加入视频流和音频流的推送。先对数据进行分析归类,也就是我们要创建的类。
第一个类:Main.as,文档类
第二个类:FMSEr.as,负责创建连接,断开连接等。
第三个类:FMSClient.as,他是NetConnection的client属性,服务端连接客户端的入口
第四个类:FMSEvent.as,他里边自定义了一些我们需要的事件。比如连接,断开,以及以后的更多的事情。
第五个类:UserListView.as,他是一个显示对象,里边用来放用户列表。
第六个类:Button.as,他是按钮对象。
第七个类:TextArea.as,他是文本区域对象。有个百色底色还有小边框。
客户端创建完了,我们还需要创建服务端。在FMS服务器的application目录下(C:\Program Files\Adobe\Flash Media Server 4.5\applications\wuziqi),创建一个wuziqi的目录,在里边创建一个main.asc的通信文件。
如果不用共享对象,客户端沟通服务端就用call。如果不想要call的结果,第二个参数通常设置设置为null。
服务端沟通客户端,如果是一对一,就用call吧。如果是广播,就用broadcastMsg广播。当然,你用for循环来call一样可以实现广播。
如果是相同账号登陆。你可以将旧账号踢出服务器,将新登陆的接入服务器。
服务端呼叫服务端的方法,用application来定义或自行构造。
客户端呼叫服务端的方法,用Client.prototype来构造。
客户端呼叫客户端,需要服务端做中转。
下边先贴出客户端代码,再贴出服务端代码。最后贴出该项目功能的展示。
package com.vini123 { import com.vini123.client.FMSEr; import com.vini123.ui.Button; import com.vini123.view.UserListView; import flash.display.Sprite; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ [SWF(width = "720" , height = "450" , frameRate="30")] public class Main extends Sprite { private var _fmser:FMSEr; private var _conBtn:Button; private var _userListView:UserListView; private var _userName:String; private var _userTxt:TextField; public function Main() { init(); } private function init():void { _userName = "vini" + int(100 * Math.random()) * int(100 * Math.random()); _fmser = new FMSEr(); _userListView = new UserListView(_userName); addChild(_userListView); _userListView.x = 20; _userListView.y = 15; _conBtn = new Button("连接"); addChild(_conBtn); _conBtn.addEventListener(MouseEvent.CLICK , clickHandler); resizeConBtn(); var str:String = "<font color='#ff0000'><b>" + "欢迎您:" + _userName + "</b></font> "; showYourName(str); _userTxt.x = 20 ; _userTxt.y = stage.stageHeight - _userTxt.textHeight - 10; } private function clickHandler(e:MouseEvent):void { switch(_conBtn.label) { case "连接": _conBtn.label = "发送"; _fmser.Connect(_userName); _userListView.txtWidth = 1; resizeConBtn(); break; case "退出": _conBtn.label = "连接"; _fmser.Disconnect(_userName); break; case "发送": _fmser.SendMessage(_userListView.outTxtText , _userListView.isSecret , _userListView.otherUser); break; } } private function resizeConBtn():void { _conBtn.x = _userListView.x + _userListView.width -5 - _conBtn.width; _conBtn.y = _userListView.y + _userListView.height - 50 ; } private function showYourName(str:String):void { _userTxt = new TextField(); _userTxt.autoSize = TextFieldAutoSize.LEFT; var txtFormat:TextFormat = new TextFormat(); txtFormat.bold = true ; txtFormat.font = "Microsoft YaHei"; txtFormat.size = 13; txtFormat.color = 0x0099ff; _userTxt.mouseEnabled = false; _userTxt.htmlText = str; _userTxt.setTextFormat(txtFormat); addChild(_userTxt); } } }
package com.vini123.client { import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.NetStatusEvent; import flash.net.NetConnection; import flash.net.NetStream; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ public class FMSEr extends EventDispatcher { private static const RTMP:String = "rtmp://localhost/wuziqi"; private var _userName:String; private var _nc:NetConnection; private var _ns:NetStream; private var _hasConnected:Boolean = false; public function FMSEr(target:IEventDispatcher=null) { super(target); } public function Connect(userName:String):void { _userName = userName; if(!_hasConnected) { if(!_nc) { _nc = new NetConnection(); _nc.client = FMSClient.instance; } _nc.connect(RTMP,_userName); _nc.addEventListener(NetStatusEvent.NET_STATUS , netStatusHandler); } } public function Disconnect(userName:String):void { if(_hasConnected) { _hasConnected = false; _nc.call("disconnect",null,userName); } } /** * * @param str * 发送消息 * */ public function SendMessage(str:String , isSecret:Boolean , otherUser:String):void { var obj:Object = {userName:_userName , isSecret:isSecret , otherUser:otherUser}; _nc.call("sendMessage" , null , obj , str); } private function netStatusHandler(e:NetStatusEvent):void { var code:String = e.info.code; trace("code:" , code); switch(code) { case "NetConnection.Connect.Success": _hasConnected = true; break; default: break; } } } }
package com.vini123.client { import com.vini123.event.FMSEvent; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ public class FMSClient extends EventDispatcher { private static var _instance:FMSClient; public function FMSClient(target:IEventDispatcher=null) { super(target); } public static function get instance():FMSClient { if(!_instance) { _instance = new FMSClient(); } return _instance; } public function hasConnected(userList:Array, userName:String, time:String):void { trace("连接成功"); dispatchEvent(new FMSEvent(FMSEvent.HAS_CONNECTED,{userList:userList , userName:userName , time:time})); } public function disconnect(userName:String , time:String):void { trace("断开"); dispatchEvent(new FMSEvent(FMSEvent.HAS_DISCONNECTED,{userName:userName,time:time})); } public function sendMessage(obj:Object , message:String , time:String):void { trace("发送消息"); dispatchEvent(new FMSEvent(FMSEvent.SEND_MESSAGE ,{obj:obj , message:message , time:time})); } } }
package com.vini123.event { import flash.events.Event; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ public class FMSEvent extends Event { public var data:Object; public static const HAS_CONNECTED:String = "hasLinked"; public static const HAS_DISCONNECTED:String = "hasDisconnected"; public static const SEND_MESSAGE:String = "sendMessage"; public function FMSEvent(type:String, data:Object , bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); this.data = data; } } }
package com.vini123.view { import com.vini123.client.FMSClient; import com.vini123.event.FMSEvent; import com.vini123.ui.Button; import com.vini123.ui.TextArea; import flash.display.Sprite; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFieldType; import flash.text.TextFormat; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ public class UserListView extends Sprite { private var _userSp:Sprite; private var _userName:String; private var _userList:Array; private var _buttonList:Vector.<Button> = new Vector.<Button>(); private var _outTxt:TextArea; private var _inputTxt:TextArea; private var _txtWidthList:Array =[680 , 550]; private var _txtWidth:int ; private var _outTxtHeight:int = 280 ; private var _inputTxtHeight:int = 95; private var _str:String = ""; private var _isSecret:Boolean = false; private var _otherUser:String; public function UserListView(userName:String) { _txtWidth = _txtWidthList[0]; _userSp = new Sprite(); addChild(_userSp); _userName = userName; addEvent(); } private function addEvent():void { _outTxt = new TextArea(); addChild(_outTxt); _outTxt.width = _txtWidth; _outTxt.height = _outTxtHeight; _inputTxt = new TextArea() ; addChild(_inputTxt); _inputTxt.width = _txtWidth - 80 ; _inputTxt.height = _inputTxtHeight ; _inputTxt.type = TextFieldType.INPUT; _inputTxt.y = _outTxt.y + _outTxt.height + 20; FMSClient.instance.addEventListener(FMSEvent.HAS_CONNECTED , hasConnectedHandler); FMSClient.instance.addEventListener(FMSEvent.HAS_DISCONNECTED , hasDisConnectedHandler); FMSClient.instance.addEventListener(FMSEvent.SEND_MESSAGE , sendMessageHandler); } private function hasConnectedHandler(e:FMSEvent):void { if(e.data.userName == _userName) { _userList = e.data.userList.slice(0,(e.data.userList.length -1)) as Array; //如果登陆进来的这个人是你自己 for(var i:int = 0 ; i< _userList.length ; i++) { var button:Button = new Button(_userList[i] , true); button.name = _userList[i]; _userSp.addChild(button); _buttonList.push(button); button.y = (button.height + 5) * i ; button.addEventListener(MouseEvent.MOUSE_DOWN , secretHandler); } } else { _userList.push(e.data.userName); var singleButton:Button = new Button(e.data.userName , true); _userSp.addChild(singleButton); _buttonList.push(singleButton); if(_userList.length !=1) { singleButton.y = _userSp.height + 5; } singleButton.name = e.data.userName; singleButton.addEventListener(MouseEvent.MOUSE_DOWN , secretHandler); } _str = "<font color='#ff0000'>【" + e.data.time + " 】</font> " + "我们热烈欢迎 " + "<font color='#ff0000'><b>" + e.data.userName + "</b></font> " + " 进来啦!"; _outTxt.htmlText = _str; } private function secretHandler(e:MouseEvent):void { if((e.currentTarget as Button).isAdd) { (e.currentTarget as Button).isAdd = false; _isSecret = false; _otherUser = ""; } else { (e.currentTarget as Button).isAdd = true; _isSecret = true; _otherUser = (e.currentTarget as Button).name ; } } private function hasDisConnectedHandler(e:FMSEvent):void { var tempName:String = e.data.userName as String; var index:int = _userList.indexOf(tempName); _userList.splice(index,1); _buttonList.splice(index ,1); _userSp.removeChildren(); for(var i:int = 0 ; i< _buttonList.length ; i++) { _buttonList[i].name = _userList[i]; _userSp.addChild(_buttonList[i]); _buttonList[i].y = (_buttonList[i].height + 5) * i ; } _str = "<font color='#ff0000'>【" + e.data.time + " 】</font> " + "我们悲伤欢送 " + "<font color='#ff0000'><b>" + e.data.userName + "</b></font> " + " 离开啦!"; _outTxt.htmlText = _str; } private function sendMessageHandler(e:FMSEvent):void { var obj:Object = e.data.obj; if(obj.isSecret) { if(obj.userName == _userName) { _str = "<font color='#ff0000'>【" + e.data.time + " 】</font> " + "你悄悄地对 "+ "<font color='#ff0000'><b>" + obj.otherUser + "</b></font> " +" 说: " + e.data.message; } else { _str = "<font color='#ff0000'>【" + e.data.time + " 】</font> " + "<font color='#ff0000'><b>" + obj.userName + "</b></font> " + "悄悄地对你说: " + e.data.message; } } else { _str = "<font color='#ff0000'>【" + e.data.time + " 】</font> " + "<font color='#ff0000'><b>" + obj.userName + " : "+ "</b></font> " + e.data.message; } _outTxt.htmlText = _str; } public function set txtWidth(value:int):void { _txtWidth = _txtWidthList[value]; _outTxt.width = _txtWidth; _inputTxt.width = _txtWidth - 80 ; if(value ==1) { _userSp.x = _txtWidthList[1] + 5 ; } } public function get outTxtText():String { var tempStr:String = _inputTxt.text; _inputTxt.text = ""; return tempStr; } public function get isSecret():Boolean { return _isSecret; } public function get otherUser():String { return _otherUser; } } }
package com.vini123.ui { import flash.display.Sprite; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-21 下午5:48:44 * */ public class Button extends Sprite { public var id:int; private var _label:String; private var _txt:TextField; private var _txtFormat:TextFormat; private var _size:int = 13; private var _txtFont:String = "Microsoft YaHei"; private var _defaultTxtColor:int = 0xffffff; private var _overTxtColor:int = 0xE32668; private var _background:Sprite; private var _backgroundColor:int = 0x0099ff; private var _frame:Sprite; private var _frameBorder:int = 0; private var _gapX:int = 10; private var _gapY:int = 4; private var _isSelect:Boolean = false; private var _isAdd:Boolean = false; private var _recFrame:Sprite; private var _checkFrame:Sprite; private var _isList:Boolean = false; public function Button(label:String , isList:Boolean = false):void { _label = label; _isList = isList; _txtFormat = new TextFormat(); _txtFormat.size = _size; _txtFormat.font = _txtFont; _txtFormat.bold = true; _txtFormat.letterSpacing = 1; _txt = new TextField(); addChild(_txt); _txt.defaultTextFormat = _txtFormat; _txt.autoSize = TextFieldAutoSize.LEFT; _txt.textColor = _defaultTxtColor; _txt.mouseEnabled = false; _txt.multiline = false; _txt.text = _label; _txt.x = _gapX ; _txt.y = _gapY * 0.6; _frame = new Sprite(); addChild(_frame); if(_isList) { drawRect(); } _background = new Sprite(); addChildAt(_background , 0); resizeDeep(); this.buttonMode = true; this.addEventListener(MouseEvent.ROLL_OVER , overHandler); this.addEventListener(MouseEvent.ROLL_OUT , outHandler); } private function drawRect():void { var tempHeight:int = _txt.textHeight -4; _recFrame = new Sprite(); _recFrame.graphics.lineStyle(1.2,_defaultTxtColor); _recFrame.graphics.beginFill(0xfff, 0); _recFrame.graphics.drawRect(0,0,tempHeight ,tempHeight); _recFrame.graphics.endFill(); addChild(_recFrame); _checkFrame = new Sprite(); _checkFrame.graphics.clear(); _checkFrame.graphics.lineStyle(1,_overTxtColor); _checkFrame.graphics.moveTo(1 , tempHeight*0.3 ); _checkFrame.graphics.lineTo(tempHeight * 0.3 ,tempHeight -1); _checkFrame.graphics.lineTo(tempHeight -1 , 0); _checkFrame.graphics.endFill(); _recFrame.addChild(_checkFrame); _recFrame.x = _txt.x + _txt.textWidth + _gapX + 2; _recFrame.y = _txt.y + 4; _checkFrame.visible = false; } private function resizeDeep():void { var tempWidth:int = _txt.textWidth + _gapX * 2 var tempHeight:int = _txt.textHeight + _gapY *2 ; if(_isList) { tempWidth = _txt.textWidth + _gapX * 3 + _recFrame.width + 5 ; } _background.graphics.clear(); _background.graphics.beginFill(_backgroundColor); _background.graphics.drawRoundRect(0,0,tempWidth , tempHeight , 5 , 5); _background.graphics.endFill(); } private function resizeFrame():void { _frame.graphics.clear(); if(_isSelect) { _frame.graphics.beginFill(0xFFFF00); _frame.graphics.drawRect(0 , 0 , _background.width ,_background.height); _frame.graphics.drawRect( _frameBorder , _frameBorder ,( _background.width - _frameBorder * 2) ,(_background.height - _frameBorder*2)); } } public function get label():String { return _label; } public function set label(value:String):void { _label = value; _txt.text = _label; resizeDeep(); } private function outHandler(e:MouseEvent):void { if(!_isSelect) { _txt.textColor = _defaultTxtColor; } } private function overHandler(e:MouseEvent):void { if(!_isSelect) { _txt.textColor = _overTxtColor; } } private function updataTxtColor():void { if(_isSelect) { _txt.textColor = _overTxtColor; } else { _txt.textColor = _defaultTxtColor; } } public function set isSelect(value:Boolean):void { _isSelect = value; updataTxtColor(); resizeFrame(); } public function get isAdd():Boolean { return _isAdd; } public function set isAdd(value:Boolean):void { _isAdd = value; if(_isAdd) { _checkFrame.visible = true; } else { _checkFrame.visible = false; } } } }
package com.vini123.ui { import flash.display.Sprite; import flash.events.Event; import flash.events.FocusEvent; import flash.events.MouseEvent; import flash.filters.DropShadowFilter; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFieldType; import flash.text.TextFormat; /** * @author vini123 * @email lichking_lin86@qq.com * @weixin vinifly * @date 2014-4-22 上午11:27:28 * */ public class TextArea extends Sprite { private var _txt:TextField; private var _txtFormat:TextFormat; private var _size:int = 13; private var _font:String = "Microsoft YaHei"; private var _color:int = 0x000000; private var _type:String = TextFieldType.DYNAMIC; private var _border:Sprite; private var _borderWidth:int = 80; private var _borderHeight:int = 45; private var _gap:int = 3; private var _dropFilter1:DropShadowFilter; private var _dropFilter2:DropShadowFilter; public function TextArea() { _border = new Sprite(); _txt = new TextField(); addChild(_border); addChild(_txt); _txtFormat = new TextFormat(); _txtFormat.leading = 10; _txtFormat.letterSpacing = 0.5; _txt.wordWrap = true; _txt.multiline = true; _txt.autoSize = TextFieldAutoSize.LEFT; _txt.type = _type; _txt.addEventListener(FocusEvent.FOCUS_IN , focusInHandler); _txt.addEventListener(FocusEvent.FOCUS_OUT , focusOutHandler); _txt.text = ""; resizeBorder(); resizeTxt(); _txtFormat.bold = false; _txt.defaultTextFormat = _txtFormat; this.addEventListener(MouseEvent.MOUSE_DOWN , mouseDownHandler); _dropFilter1 = new DropShadowFilter(); _dropFilter1.distance = 1.2; _dropFilter1.angle = 45; _dropFilter1.blurX = 2; _dropFilter1.blurY = 2; _dropFilter1.color = 0xaaaaaa; _dropFilter2 = new DropShadowFilter(); _dropFilter2.distance = -1.2; _dropFilter2.angle = 45; _dropFilter2.blurX = 3; _dropFilter2.blurY = 3; _dropFilter2.color = 0xcccccc; this.filters= [_dropFilter1 , _dropFilter2]; } private function focusInHandler(e:FocusEvent):void { if(_type == TextFieldType.INPUT) { addEventListener(Event.ENTER_FRAME , enterHandler); } } private function focusOutHandler(e:FocusEvent):void { removeEventListener(Event.ENTER_FRAME , enterHandler); } private function enterHandler(e:Event):void { _txt.setTextFormat(_txtFormat); } private function mouseDownHandler(e:MouseEvent):void { stage.focus = _txt; } public function set htmlText(str:String):void { _txt.htmlText += (str + "\n") ; } private function resizeBorder():void { _border.graphics.clear(); _border.graphics.beginFill(0xffffff,1); _border.graphics.drawRect(0 , 0 , _borderWidth , _borderHeight); _border.graphics.endFill(); _txt.x = _gap ; _txt.y = _gap ; _txt.width = _borderWidth - _gap * 2 ; _txt.height = _borderHeight - _gap *2 ; } private function resizeTxt():void { _txtFormat.font = _font; _txtFormat.color = _color; _txtFormat.size = _size; _txtFormat.bold = true; _txt.setTextFormat(_txtFormat); } public override function set width(value:Number):void { _borderWidth = value; resizeBorder() ; } public override function set height(value:Number):void { _borderHeight = value; resizeBorder(); } public function get font():String { return _font; } public function set font(value:String):void { _font = value; resizeTxt(); } public function get color():int { return _color; } public function set color(value:int):void { _color = value; resizeTxt(); } public function get size():int { return _size; } public function set size(value:int):void { _size = value; resizeTxt(); } public function get type():String { return _type; } public function set type(value:String):void { _type = value; _txt.type = _type; } public function get text():String { return _txt.text; } public function set text(value:String):void { _txt.text = value; } } }
通信文件:main.asc
var nameList; var playerList; application.onAppStart=function() { nameList = []; playerList = []; } //连接 application.onConnect = function(client , userName) { var len = application.clients.length; if(len >0) { for(var i = 0 ; i< len ; i++) { if(application.clients[i].name == userName) { trace("断开,这里是相同账号登陆。断开旧的,登陆新的:" + userName); nameList.splice(nameList.indexOf(userName) , 1); //将名字从数字中删除 application.disconnect(application.clients[i]); //并将原来的那个人断开连接。然会跳出for循环 break; } } } client.name = userName; //给client一个name属性 application.acceptConnection(client); //接受用户的请求连接 nameList.push(userName); application.broadcastMsg("hasConnected",nameList, userName , application.getNowTime()); //广播一条消息 } //断开 application.onDisconnect = function(client) { var index = nameList.indexOf(client.name); trace("当客户端断开,会响应这里:" + client.name); if(index >=0) { nameList.splice(index,1) ; } index = playerList.indexOf(client.name); if(index >=0 ) { playerList.indexOf(index,1) ; } application.broadcastMsg("disconnect",client.name , application.getNowTime()); //广播一条消息,服务端也可以call客户端的 } Client.prototype.sendMessage = function(obj, str) { if(obj.isSecret == true) { trace("这里是私聊"); var i = 0 ; var len = application.clients.length; for(i = 0 ; i< len ; i++) { if(application.clients[i].name == obj.userName) { application.clients[i].call("sendMessage" , null , obj , str , application.getNowTime()) } else if(application.clients[i].name == obj.otherUser) { application.clients[i].call("sendMessage" , null , obj , str , application.getNowTime()) } } } else { trace("这里是公共聊天"); application.broadcastMsg("sendMessage" , obj , str , application.getNowTime()); } } //客户端call过来,断开连接 Client.prototype.disconnect = function(userName) { var len = application.clients.length; for(var i = 0 ; i< len ; i++) { if(application.clients[i].name == userName) { //客户端点击按钮主动断开连接。暂时不使用这样断开 application.disconnect(application.clients[i]); break; } } } //服务端调用服务端的方法 application.getNowTime = function () { var timedate = new Date(); var hours = timedate.getHours(); var minutes = timedate.getMinutes(); var seconds = timedate.getSeconds(); if(hours < 10) { hours = "0" + hours; } if(minutes < 10) { minutes = "0" + minutes; } if(seconds < 10) { seconds = "0" + seconds; } return hours+":"+minutes+":"+seconds; } Array.prototype.indexOf = function(k) { var len = this.length; var index = -1; for(var i=0;i<len;i++) { if(k == this[i]) { index = i; break; } } return index; }
欢迎加我微信哦:vinifly