zoukankan      html  css  js  c++  java
  • flash player10.1 + FMS4中的p2p功能

    在fms4以前Adobe只允许在stratus中才能使用p2p功能。令人高兴的是,在最新发布的fms4中,p2p功能已经集成进来了,这将给实时视频类的应用带来更高的效率,adobe这次很给力!

    为了使用p2p,开发用的flex sdk至少要4.1以上(当然最高版本是代号为hero的4.5版本,可从adobe的官网下载),另外还需要fms4(同样可从adobe官网下载开发版本)。

    先上完整代码吧:

    package {
    
    	import fl.controls.Button;
    	import fl.controls.Label;
    	import fl.controls.TextArea;
    	import flash.display.Sprite;
    	import flash.events.MouseEvent;
    	import flash.events.NetStatusEvent;
    	import flash.net.GroupSpecifier;
    	import flash.net.NetConnection;
    	import flash.net.NetGroup;
    	import flash.net.NetGroupReplicationStrategy;
    	import flash.text.TextFormat;
    
    
    	public class p2p_HelloWorld extends Sprite {
    		
    		private var _lbl:Label;
    		private var _btnAddToWant:Button;
    		private var _btnGenData:Button;
    		private var _btnAddToHave:Button;
    		private var _txtObj:TextArea;
    		private var _txtOutput:TextArea;
    		private var _data:Vector.<String>;
    		private var _dataLength:uint = 100;
    		private var _nc:NetConnection;
    		private var _ng:NetGroup;
    		private var _spec:GroupSpecifier;
    		private var _server:String = "rtmfp://localhost/HelloServer";
    		private var _groupName:String = "myGroup";
    		private var _connected:Boolean = false;
    
    		public function p2p_HelloWorld(){
    			init();
    		}
    
    		private function init():void {
    			this._btnAddToWant = btnAddToWant;
    			this._btnAddToHave = btnAddToHave;
    			this._btnGenData = btnGenData;
    			this._txtObj = txtObj;
    			this._txtOutput = txtOutput;
    			this._lbl = lbl;
    			
    			var style:TextFormat = new TextFormat("宋体", 12, 0x000000,false,false,false,null,null,null,null,null,null,5);
    			this._btnAddToHave.setStyle("textFormat", style);
    			this._btnAddToWant.setStyle("textFormat", style);
    			this._btnGenData.setStyle("textFormat", style);
    			this._txtObj.setStyle("textFormat", style);
    			this._txtOutput.setStyle("textFormat", style);
    			this._lbl.setStyle("textFormat", style);
    			
    			this._btnGenData.addEventListener(MouseEvent.CLICK, _btnGenData_Click);
    			this._btnAddToHave.addEventListener(MouseEvent.CLICK, _btnAddToHave_Click);
    			this._btnAddToWant.addEventListener(MouseEvent.CLICK, _btnAddToWant_Click);
    
    			//先连接到服务器
    			_nc = new NetConnection();
    			_nc.addEventListener(NetStatusEvent.NET_STATUS, _nc_Net_Status);
    			_nc.connect(_server);
    			output("正在连接 " + _server + " ...");
    		}
    
    		private function _nc_Net_Status(e:NetStatusEvent):void {
    			output(e.info.code);
    			switch (e.info.code){
    				case "NetConnection.Connect.Success":	
    					//连接成功后,要设置NetGroup
    					this._spec = new GroupSpecifier(this._groupName);					
    					_spec.serverChannelEnabled = true;//设置允许创建到服务端的通道
    					_spec.objectReplicationEnabled = true;//允许对象复制
    					_ng = new NetGroup(_nc, _spec.groupspecWithAuthorizations());
    					_ng.addEventListener(NetStatusEvent.NET_STATUS, _nc_Net_Status);
    					break;
    				case "NetGroup.Connect.Success":
    					_connected = true;
    					_ng.replicationStrategy = NetGroupReplicationStrategy.LOWEST_FIRST;//设置数据块传输时,先传递索引号最小的块
    					break;
    				case "NetGroup.Replication.Fetch.SendNotify": 
    					//每当"接收方"有数据到达(但尚未开始接收)时,将触发此处理
    					output("    -->通知:数据块 " + e.info.index + "  即将被接收");
    					break;
    				case "NetGroup.Replication.Fetch.Failed": 
    					//“接收方”有数据接收失败时,将触发此处理
    					output("    -->错误:数据块 " + e.info.index + " 接收失败");
    					break;
    				case "NetGroup.Replication.Fetch.Result": 
    					//“接收方”每次成功接收到数据时,触发此段处理
    					output("    -->数据块 " + e.info.index + " 已成功接收,值:" + e.info.object);
    					_ng.addHaveObjects(e.info.index, e.info.index);	//接收完成以后,将接收到的数据加入“待发送对象列表"中,这样人越多,传输越稳定,速度也越快	
    					if (_data == null) {
    						_data = new Vector.<String>(this._dataLength);
    					}
    					_data[e.info.index] = e.info.object.toString();
    					//说明全部接收完了
    					if (e.info.index == this._dataLength - 1) {
    						for (var i:int = 0; i < _dataLength; i++){
    							_data[i] = "这是数据 " + i.toString();
    							this._txtObj.appendText("index:" + i.toString() + ",data:" + _data[i] + " | ");
    						}
    					}
    					break;
    				case "NetGroup.Replication.Request": 
    					//每当有数据传输请求时,“提供方”将触发此处理
    					_ng.writeRequestedObject(e.info.requestID, _data[e.info.index]);//这里才是真正的响应“接收方",将指定的数据发送过去
    					output("    -->数据块 " + e.info.index + " 请求被发送,本次请求ID:" + e.info.requestID);
    					break;
    				default:
    					break;
    			}
    		}
    
    		//初始化生成数据
    		private function _btnGenData_Click(e:MouseEvent):void {
    			this._txtObj.text = "";
    			if (_data==null){
    				_data = new Vector.<String>(this._dataLength);
    			}
    			for (var i:int = 0; i < _dataLength; i++){
    				_data[i] = "这是数据 " + i.toString();
    				this._txtObj.appendText("index:" + i.toString() + ",data:" + _data[i] + " | ");
    			}
    		}
    		
    		//将生成的初始数据,添加到待发送的“列表”中
    		private function _btnAddToHave_Click(e:MouseEvent):void 
    		{
    			this._ng.addHaveObjects(0, _dataLength - 1);
    		}
    		
    		//请求接收数据
    		private function _btnAddToWant_Click(e:MouseEvent):void
    		{
    			this._ng.addWantObjects(0, _dataLength - 1);
    		}
    
    		//输出结果
    		private function output(s:String):void {
    			this._txtOutput.appendText(s + "\n");
    		}
    	}
    
    }
    
    

    在这段代码中我们看到了一个全新的NetGroup对象,要使用p2p,“接收方”与“接收方”必须先加入到“相同名称"的NetGroup中。而且要发送的数据,必须分解有顺序的一块一块(通常用有序数组来保存这些数据块),然后"发送方"调用addHaveObjects方法设置待发送的数据块,而"接收方"则调用addWantObjects请求需要接收的块。

    一旦"接收方"调用了addWantObjects方法后,"发送方"便会进入"NetGroup.Replication.Request"状态,此时"发送方"响应"接收方"的请求,将需要的数据块以udp协议发送过去,然后“接收方”会收到"NetGroup.Replication.Fetch.SendNotify"的数据到达通知,如果成功接收,将进入“NetGroup.Replication.Fetch.Result”状态,全部接收完成后,开发人员可根据需要将这些块重新合并成原始对象。

    处理过程示意图如下:

    文中代码最终的运行截图:

    测试方法:发送方先点击“生成初始数据”,然后点击“添加要发送的数据”,最后接收方点击“接收数据”

    此外,如果多开几个"接收方",可以验证一下“接收方”收到数据后是否能变成数据提供者,向其它接收方提供数据,也就是所谓的p2p中"人越多,速度越快,传输越稳定"的现象

    但是,FMS4中的p2p也不是完美无缺,实际测试下来,目前尚不能打洞,即所有peer端如果在同一个网段,传输是正常的,但是如果不是同一个网段则无法进行p2p。

    不过,如果参与p2p的机器越多,接收到数据的客户端根据文中的代码处理,也可以变成发送方,这表示有可能本来在同一个网段的其它用户原本没有数据来源,但是只要本网段有一个用户接收到数据后(比如这个用户有多重网络),本网段的其它用户也能接收数据了,这在一程度上能解决打洞的矛盾。

    示例源文件下载:https://files.cnblogs.com/yjmyzz/p2pTest.7z

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时
    【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时
    【Linux开发】linux设备驱动归纳总结(七):2.内核定时器
    【Linux开发】linux设备驱动归纳总结(七):2.内核定时器
    【Linux开发】linux设备驱动归纳总结(八):1.总线、设备和驱动
    【Linux开发】linux设备驱动归纳总结(八):1.总线、设备和驱动
    【Linux开发】linux设备驱动归纳总结(六):1.中断的实现
    【Linux开发】linux设备驱动归纳总结(六):1.中断的实现
    【Linux开发】linux设备驱动归纳总结(六):2.分享中断号
    【Linux开发】linux设备驱动归纳总结(六):2.分享中断号
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1888072.html
Copyright © 2011-2022 走看看