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

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

先上完整代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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的机器越多,接收到数据的客户端根据文中的代码处理,也可以变成发送方,这表示有可能本来在同一个网段的其它用户原本没有数据来源,但是只要本网段有一个用户接收到数据后(比如这个用户有多重网络),本网段的其它用户也能接收数据了,这在一程度上能解决打洞的矛盾。

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

FMS4中的P2P功能的更多相关文章

  1. Android端IM应用中的@人功能实现:仿微博、QQ、微信,零入侵、高可扩展

    本文由“猫爸iYao”原创分享,感谢作者. 1.引言 最近有个需求:评论@人(没错,就是IM聊天或者微博APP里的@人功能),就像下图这样:   ▲ 微信群聊界面里的@人功能    ▲ QQ群聊界面里 ...

  2. Windows 10 开发人员预览版中的新增功能(转自 IT之家)

    Windows 10 开发人员预览版中的新增功能 在Win10预览版中安装工具与SDK后,即可着手创建Windows通用应用或先浏览目前的环境与此前相比都发生了什么变化. 应用建模 文件资源管理器: ...

  3. ADO.NET 中的新增功能

    ADO.NET 中的新增功能: .NET Framework (current version) 以下是 .NET Framework 4.5 中 ADO.NET 的新增功能. SqlClient D ...

  4. CSS3中的动画功能(一)

    css3中的动画功能分为transitions功能和animations功能,这两种功能都可以通过改变css属性值来产生动画效果.今天带大家一起来看看css3动画功能中的transitions的用法. ...

  5. 使用vs中的发布功能发布asp.net core项目时遇到ERROR_CERTIFICATE_VALIDATION_FAILED错误

    今天将VS2015编制的一个asp.net core项目发布到服务器进行测试,使用的是vs中主菜单"生成"中的"发布"功能. 遇到了一个错误,在网上反复检索尝试 ...

  6. SQLSERVER2014中的新功能

    SQLSERVER2014中的新功能 转载自:http://blog.csdn.net/maco_wang/article/details/22701087 博客人物:maco_wang SQLSER ...

  7. Eclipse 中的重构功能

    Eclipse 中的重构功能使其成为了一个现代的 Java 集成开发环境 (IDE),而不再是一个普通的文本编辑器.使用重构,您可以轻松更改您的代码,而不必担心对别处造成破坏.有了重构,您可以只关注于 ...

  8. 在Sharepoint 2010中启用Session功能的说明文档

    在Sharepoint 2010中启用Session功能的说明文档 开发环境:Windows 7系统,SharePoint Server 2010,Visual Studio 2010 按以下步骤进行 ...

  9. WPF4.5 中的新增功能和增强功能的信息

    本主题包含有关 Windows Presentation Foundation (WPF) 版本 4.5 中的新增功能和增强功能的信息. 本主题包含以下各节: 功能区控件 改善性能,当显示大时设置分组 ...

随机推荐

  1. 个人学习FPGA的初步过程

    对于FPGA,完全是从零开始学习,简单讲述一下我个人学习FPGA的经历吧: 没有开发板的日子.说真的要我掏腰包买开发板觉得是一件非常奢侈的事情.理由1:现成的东西,背后影藏诸多诡异的事情我们是无法体会 ...

  2. Lua学习笔记4. coroutine协同程序和文件I/O、错误处理

    Lua学习笔记4. coroutine协同程序和文件I/O.错误处理 coroutine Lua 的协同程序coroutine和线程比较类似,有独立的堆栈.局部变量.独立的指针指令,同时又能共享全局变 ...

  3. JS基础知识——缓动动画

    基于距离的缓动动画 原理:设定起始位置  start 和终止位置 end,变化会越来越慢. 公式:start=start+(end-start)/10;     这个10不是固定的,想分成多少份就分成 ...

  4. ios开发证书,描述文件,bundle ID的关系

    苹果为了控制应用的开发与发布流程,制定了一套非常复杂的机制.这里面的关键词有:个人开发者账号,企业开发者账号,bundle ID,开发证书,发布证书(又叫"生产证书"),开发描述文 ...

  5. Ubuntu 12.04下PHP环境的搭建(LAMP)

    1.首先打开命令行,切换到root身份,获得最新的软件包 su root sudo apt-get install update 2.安装MySQL数据库 sudo apt-get install m ...

  6. lpc1788IO口模拟IIC

    #ifndef __MYIIC_H_ #define __MYIIC_H_ #include "common.h" #include "delay.h" #in ...

  7. Android L(5.0)源码之手势识别OnTouchListener

    在Activity中,因为要监听触摸屏的触摸事件和手势时间,所以该Activity必须实现OnTouchListener和OnGestureListener两个接口,并重写其中的方法.本人根据andr ...

  8. 关于iOS开发中info.plist文件的解读

    我们建立一个工程后,会在Supporting files下面看到一个"工程名-Info.plist"的文件,这个是对工程做一些运行期配置的文件,很重要,不能删除.  下面就对其ke ...

  9. sgu194 Reactor Cooling【无源汇有上下界可行流】

    这是模板题了吧,先建立附加源汇,然后保留每个点的in-out,如果这个值是正的,那么就从附加源先这个点连一个边权为in-out的边,否则从这个点向附加汇连一条相反数的边,剩下题目中的边就用上界-下界连 ...

  10. Thinking in scala (1)----类

    ChecksumAccumulator.scala import scala.collection.mutable.Map class ChecksumAccumulator { private va ...