最近在做一个基于BS结构的视频会议系统,决定采用开源的FluorineFx.net与Flex结合的方法进行开发,前期开发都非常顺利,包括同步白板等。但到了实时视频传输的时候,原本设计是每个客户端可以显示三路视频,但到IIS上测试的时候,发现状态很不稳定,偶尔可以全部显示出来,大部分情况下,客户端总是随机显示一个或两个。查询了ActionScript的技术文档、FluorineFx的技术文档等,也没有找出与这个问题相关的描述,包括网络上,对NetConnection与NetStream的对应关系也没有很肯定的说明或总结。程序中实在找不出问题的所在,我初步怀疑是浏览器并发连接数限制的问题导致无法在内置于浏览器的swf中同时连接多路视频,于是决定将这部分抽象出来,专门做一个测试,测试选择了三个比较具有代表性的浏览器进行。

开发环境:VS2008(FluorineFx.Net)与FlexBuilder3

测试环境:IE 8,Chrome,FF + Flash player 11 Debug版

  一、测试用的流处理服务程序编写

首先利用Vs 2008新建了一个FluorineService项目,并添加应用程序适配器类,在RTMP连接AppConnect时用来判断连接的合法性,即只有当rtmp连接的parameters[1]为"000"的连接请求才允许连接服务器上的rtmp信道端口,如下:

using FluorineFx;
using FluorineFx.Messaging.Adapter;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Api.SO; namespace OnLineService
{
public class DemoApp:ApplicationAdapter
{ public override bool AppConnect(IConnection connection, object[] parameters)
{
string userName = parameters[0] as string;
string password = parameters[1] as string; // 这里可以自定义登录判断验证逻辑,此处只要密码为000即可进入
if (password != "000")
return false;
return true;
} } }

新建一个FluorineFx Web后,修改其Web-INF/Flex文件夹下的services-config.xml文档,指定rtmp传输信道所用的端口:

        <channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
<endpoint uri="rtmp://{server.name}:2037" class="flex.messaging.endpoints.RTMPEndpoint"/>
<properties>
<idle-timeout-minutes>20</idle-timeout-minutes>
</properties>
</channel-definition>

在FluorineFx Web项目根目录下添加apps文件夹,在apps下添加OnLineUser文件夹,在OnLineUser下添加app.config配置文件及一个streams文件夹,streams下放入五个flv视频文件(我这里自己随便用Flex写的小程序采集了一小段视频,并复制成5份放在这个文件夹下,分别命名为mytest1...)

在app.config中指定流处理的服务程序为DemoApp,如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<application-handler type="OnLineService.DemoApp"/>
</configuration>

编译后启动调试,使Asp.Net Development Server处于运行状态(以提供流处理的服务)

二、测试用Flex项目的开发
    打开Flex Builder3后,新建一个Flex项目,项目路径设置在“一”中建立的FluorineFx Web项目的根目录下的Flex目录,并引入相关libs与编译参数,如下图所示:

然后将mxml文件编写如下:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="1254" height="246" backgroundGradientAlphas="[1.0, 1.0]"
backgroundGradientColors="[#000000, #049FF1]" fontSize="12" initialize="init()">
<mx:HBox id="hbox" x="10" y="31" width="1234" height="187">
</mx:HBox>
<mx:Script source="FiveConnFiveStream.as"/>
</mx:Application>

添加一个FiveConnFiveStream.as文件,在其下建立五个NetConnection与五个NetStream,分别连接到“一”中的五个视频文件,如下:

import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.NetStream;
import mx.controls.VideoDisplay;
import mx.messaging.config.ServerConfig;
private var rtmpChanner:String;
private var nc:NetConnection;
private var nc2:NetConnection;
private var nc3:NetConnection;
private var nc4:NetConnection;
private var nc5:NetConnection;
private var ns1:NetStream;
private var ns2:NetStream;
private var ns3:NetStream;
private var ns4:NetStream;
private var ns5:NetStream; private function init():void
{
rtmpChanner=ServerConfig.getChannel("my-rtmp").endpoint+"/OnLineUser";
nc=new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS,OnConnHandler);
nc.client=this;
nc.connect(rtmpChanner,"adsfasdf","000"); nc2=new NetConnection();
nc2.addEventListener(NetStatusEvent.NET_STATUS,OnConnHandler2);
nc2.client=this;
nc2.connect(rtmpChanner,"adsfadasdf","000"); nc3=new NetConnection();
nc3.addEventListener(NetStatusEvent.NET_STATUS,OnConnHandler3);
nc3.client=this;
nc3.connect(rtmpChanner,"adsfffasdf","000"); nc4=new NetConnection();
nc4.addEventListener(NetStatusEvent.NET_STATUS,OnConnHandler4);
nc4.client=this;
nc4.connect(rtmpChanner,"adfgg","000"); nc5=new NetConnection();
nc5.addEventListener(NetStatusEvent.NET_STATUS,OnConnHandler5);
nc5.client=this;
nc5.connect(rtmpChanner,"adfaaa","000");
} private function OnConnHandler(event:NetStatusEvent):void
{
trace("连接一状态:"+event.info.code);
if(event.info.code=="NetConnection.Connect.Success")
{
var vds:VideoDisplay=new VideoDisplay();
vds.height=160;
vds.width=240;
var vd:Video=new Video();
vd.width=240;
vd.height=160;
this.ns1=new NetStream(this.nc);
this.ns1.addEventListener(NetStatusEvent.NET_STATUS,OnStreamHandler1);
vd.attachNetStream(this.ns1);
this.ns1.client=this;
this.ns1.play("mytest1");
vds.addChild(vd);
this.hbox.addChild(vds);
trace("播放一");
}
} private function OnConnHandler2(event:NetStatusEvent):void
{
trace("连接二状态:"+event.info.code);
if(event.info.code=="NetConnection.Connect.Success")
{
var vds2:VideoDisplay=new VideoDisplay();
vds2.height=160;
vds2.width=240;
var vd2:Video=new Video();
vd2.width=240;
vd2.height=160;
this.ns2=new NetStream(this.nc2);
this.ns2.addEventListener(NetStatusEvent.NET_STATUS,OnStreamHandler2);
vd2.attachNetStream(this.ns2);
this.ns2.client=this;
this.ns2.play("mytest2");
vds2.addChild(vd2);
this.hbox.addChild(vds2);
trace("播放二");
}
} private function OnConnHandler3(event:NetStatusEvent):void
{
trace("连接三状态:"+event.info.code);
if(event.info.code=="NetConnection.Connect.Success")
{
var vds3:VideoDisplay=new VideoDisplay();
vds3.height=160;
vds3.width=240;
var vd3:Video=new Video();
vd3.width=240;
vd3.height=160;
this.ns3=new NetStream(this.nc3);
this.ns3.addEventListener(NetStatusEvent.NET_STATUS,OnStreamHandler3);
vd3.attachNetStream(this.ns3);
this.ns3.client=this;
this.ns3.play("mytest3");
vds3.addChild(vd3);
this.hbox.addChild(vds3);
trace("播放三");
}
} private function OnConnHandler4(event:NetStatusEvent):void
{
trace("连接四状态"+event.info.code);
if(event.info.code=="NetConnection.Connect.Success")
{
var vds4:VideoDisplay=new VideoDisplay();
vds4.height=160;
vds4.width=240;
var vd4:Video=new Video();
vd4.width=240;
vd4.height=160;
this.ns4=new NetStream(this.nc4);
this.ns4.addEventListener(NetStatusEvent.NET_STATUS,OnStreamHandler4);
vd4.attachNetStream(this.ns4);
this.ns4.client=this;
this.ns4.play("mytest4");
vds4.addChild(vd4);
this.hbox.addChild(vds4);
trace("播放四");
}
} private function OnConnHandler5(event:NetStatusEvent):void
{
trace("连接五状态"+event.info.code);
if(event.info.code=="NetConnection.Connect.Success")
{
var vds5:VideoDisplay=new VideoDisplay();
vds5.height=160;
vds5.width=240;
var vd5:Video=new Video();
vd5.width=240;
vd5.height=160;
this.ns5=new NetStream(this.nc5);
this.ns5.addEventListener(NetStatusEvent.NET_STATUS,OnStreamHandler5);
vd5.attachNetStream(this.ns5);
this.ns5.client=this;
this.ns5.play("mytest5");
vds5.addChild(vd5);
this.hbox.addChild(vds5);
trace("播放五");
}
} private function OnStreamHandler1(event:NetStatusEvent):void
{
trace("流一状态:"+event.info.code);
}
private function OnStreamHandler2(event:NetStatusEvent):void
{
trace("流二状态:"+event.info.code);
}
private function OnStreamHandler3(event:NetStatusEvent):void
{
trace("流三状态:"+event.info.code);
}
private function OnStreamHandler4(event:NetStatusEvent):void
{
trace("流四状态:"+event.info.code);
}
private function OnStreamHandler5(event:NetStatusEvent):void
{
trace("流五状态:"+event.info.code);
}

三、开始测试
  在FlexBuilder中Builder后,进行Debug调试。

此时,我们就可以看到在浏览器中已经可以实时加载视频了,为了我们测试的目的与结果,我们要设计如下几种测试以验证结果及进行对比:

a、排除服务器的因素,即服务器是否能提供多个并发连接的能力(以证明客户端有的实时视频显示不出非服务器的原因引起)

b、场景一:在Flex中建立5个NetConnection与5个NetStream,5个NetStream分别指向5个视频文件(验证客户端连接建立能力及每个连接加载实时流后,浏览器可维持的连续的并发连接的能力)

c、场景二:在Flex中建立5个NetConnection与5个NetStream,5个NetStream同时指向一个视频文件(验证是否可以将多个连接的并发连接同时关联服务器上同一个文件)

d、场景三:在Flex中建立1个NetConnection,并以这个NetConnection建立5个NetStream,分别测试指向一个视频文件与5个视频文件时的表现

e、场景四:将bcd中的五个都改为3个连接与3个流做对比测试

(“二”为b场景一的as代码,其它场景的在附件中有,这里不一一列出。)

注:以上几个场景,除a外,其它都分别编译后,在IE8,FF,Chrome中进行测试(IE的话还需要对注册表中连接数进行修改再次测试),每个单独的测试刷新10次,记录客户端实时连接并成功显示的视频数,并且为了排除缓存的影响,每次测试前都清空各个浏览器的缓存。

首先排除服务器的影响,我们对服务器进行连接的压力测试,同时打开多个浏览窗口,同时连接这个应用,看是不是可以支持多于5路的视频传输,测试效果如下(排除服务器因素,证明FlourineFx.Net并没有论坛中所言的连接性能诡异的问题,但这并不是压力测试,我这里的目的只是为了证明服务器的rtmp应用程序适配器可以支持5路以上的实时连接,下图中三个浏览器同时打开,并访问这个应用,可以轻易看到10个连接)

以下为随机截取的几个测试场景的图片与Flex Builder Console中的输出情况

(IE8在没有修改注册表连接数前,用5个连接与5个NetStream连接服务器时的情况,成功进行了4个并发的实时连接)

(在Flex中只用一个NetConnection,建立5个NetStream,不管是连接一个文件还是5个文件,在各个浏览器中,都只有最后一个NetStream可以成功连接上,由此证明,如果要连接实时视频数据流时,一个NetConnection只能与一个NetStream相关联,如下图)

四、测试结果统计

以下为测试结果的统计表格:

IE8在没有修改注册表连接数之前:

IE8修改注册表,将连接数改为10之后

(IE8的注册表连接数修改:HKEY_CURRENT_USER/SoftWare/Microsoft/Windows/CurrentVerSion/InternetSettings下添加两个DWORD,MaxConnectionsPer1_OServer与MaxConnectionsPerServer,分别指定其值即可指定连接数,如下:

)

Chrome的测试数据

FF的测试数据

五,总结

1、在Flex开发中,一个NetConnection只能在同一时间被一个NetStream占用,用来传输实时数据

2、现在的浏览器一般都至少能稳定地支持两个并发连接,但跟某些网站上所言的,支持几个并发连接的能力,并不是稳定的,在我上述的测试中,五个NetConnection都可以建立成功,但若利用这些连接通道同时并发传输数据,并不能保证每次都能成功建立起这些并发连接。

另附:我查阅了一下HTTP/1.1 RFC中的相关章节8.1.4,其中规定单客户端与任何服务器或代理的连接不应超过两个(single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.)。但现在的浏览器基本上都超过了这个并发连接数,以下为国外某网站上列出的各浏览器支持的并发连接数表格截图:

但经过我上面的测试发现,对于并发的且长时间被占用的连接,浏览器一般只能稳定地支持两个,不管是IE\Chrome\FF,都是如此,或许浏览器方所宣称的支持多路并发连接的能力,是对于那些突发式的、不需要长时间占用这个连接来传送大量数据而言的。

做以上测试花了一上午时间,因此写个总结在这里。原创文章,转载请注明一下。观点可能有些片面,不正确之处望指正。

附:测试中用的源文件下载 (注:以上不同的测试场景请修改mxml中引入不同的as文件)

 

Flex中NetConnection与NetStream的关系、及浏览器并发连接数测试[转]的更多相关文章

  1. SharedObject使用:在FluorineFx.net与Flex中使用共享对象维护在线用户列表实例【转】

    一.添加一个新的FluorineFx的服务类项目OnLineService,删除原有的Sample.cs,并添加一个用户类定义与一个ApplicationAdpater类:如下: /*-- User. ...

  2. Flex中设置Camera的视频清晰质量的最佳配合

    今天需要设置Flex中的Camera组件的一些属性,后来发现清晰度不是很高,于是捣鼓了上午半天,设置了很多的参数,竟然发现手册上就是有现成的一些设置方法,郁闷!不过我还是专门设置了几个有用和必要的属性 ...

  3. Python类中super()和__init__()的关系

    Python类中super()和__init__()的关系 1.单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(sel ...

  4. ArcGIS for Flex中引入google map作底图

    上篇文章到在ArcGIS View中引入google map,这里讲ArcGIS for Flex中引入google map作底图. 同样道理,以google map作底图,需要编写继承自TiledM ...

  5. 转iOS中delegate、protocol的关系

    iOS中delegate.protocol的关系 分类: iOS Development2014-02-12 10:47 277人阅读 评论(0) 收藏 举报 delegateiosprocotolc ...

  6. AS与JS相互通信(Flex中调用js函数)

    转载自http://www.blogjava.net/Alpha/archive/2009/06/27/284373.html Flex中As调用Js的方法是:     1.导入包 (import f ...

  7. Flex中 Array 的IndexOf 的作用

    Flex中 Array 的IndexOf 的作用 1.说明    indexOf用于在索引中从小到大查找,假设查得到就返回索引值,查不到就返回-1: 2.实例 (1)设计源代码 <?xml ve ...

  8. java中接口与多重继承的关系

    在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制.正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力.abstract class和int ...

  9. Flex中的折线图

    1.问题背景 在Flex中,制作一个折线图.而且给折线图的横轴和纵轴进行样式设置,详细实现过程例如以下: 2.实现实例 (1)设置横轴样式和数据绑定 <mx:horizontalAxis> ...

随机推荐

  1. ctype库试运行

    from ctypes import * msvcrt=cdll.msvcrt message_string="Hello world!\n" msvcrt.wprintf(&qu ...

  2. 【加解密】关于DES加密算法的JAVA加密代码及C#解密代码

    JAVA加密: package webdomain; import java.security.Key; import java.security.spec.AlgorithmParameterSpe ...

  3. Select模型及tcp select模型

    参考:http://m.blog.csdn.net/article/details?id=51420015 一.套接字模式 套接字模式简单的决定了操作套接字时,Winsock函数是如何运转的.Wins ...

  4. java开发eclipse常见问题(一)The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    最近刚开始用Eclipse开发,刚开始都是按教程一步一步的新建web工程也没出现什么问题. 今天选了一个新的workspace,建了个web工程发现最简单的jsp页面都报错:The superclas ...

  5. python中使用多继承

    python中使用多继承,会涉及到查找顺序(MRO).重复调用(钻石继承,也叫菱形继承问题)等 MRO MRO即method resolution order,用于判断子类调用的属性来自于哪个父类.在 ...

  6. WS之cxf简单实现

    1.服务端实现: 1.1 定义接口,用@WebService修饰: /** @WebService 所修饰的接口,那么接口里面的方法全部都属于web的服务  */ @WebService public ...

  7. geeksforgeeks@ Maximum Index (Dynamic Programming)

    http://www.practice.geeksforgeeks.org/problem-page.php?pid=129 Maximum Index Given an array A of int ...

  8. 数据结构 -- 简单图的实现与遍历 (Java)

    ---恢复内容开始--- 作者版权所有,转载请注明出处,多谢. http://www.cnblogs.com/Henvealf/p/5534071.html 前些天因为某些原因,就试着回想一下图(gr ...

  9. CSRF的攻击与防御(转)

    add by zhj:CSRF之所有发生,是因为http请求中会自动带上cookies,我的解决办法是:前端不要将数据放在cookie中,而是放在其它本地存储 (HTML5中称之为Web Storag ...

  10. pyQt 每日一练习 -- 登录框

    #coding=utf-8 #第一个练习,登录框 import sys from PyQt4 import QtGui,QtCore #登录框 class LoginBox(QtGui.QWidget ...