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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2010-11-25 21:43  菩提树下的杨过  阅读(5624)  评论(6编辑  收藏  举报