本类共有 4110 篇文章,今日更新 0

使用FDO封装XML&ADO实现与服务端数据通信_使用FDO封装XML&ADO实现与服务端数据通信

[ 来源:http://www.91now.com/down/ | 作者: | 时间:2007-5-21 18:18:01 | 浏览: 人次 ]


什么是FDO?我不知道别人有没有说过这个概念,反正我自己想了这个名字:Flash Data Operator,也不知道在Flash V2组件中是不是有了。FDO是一组类,通过这组类与服务端的配合可以在Flash中非常简单的实现和服务端通信,这是我的想法。不过这个想法源字邹邹,是在Flash8论坛上的一个朋友,他有一个东西叫我改改,呵呵,诶,能力有限,看不清眼花缭乱的代码,呵呵,于是就想着做一个FDO方便的来处理服务端数据。

FDO第一个问题就是安全性,难道我要在客户端拼装SQL 那是很不现实的问题,这样似乎太不安全了,很明显。所以应该使用暗号,外加参数。比如说,一个暗号:show me the money

可以对应一个SQL语句:select * from money where user=’?’,这种写法有点类似java中的PreparedStatement SQL的写法,?可以用参数代入。呵呵,这是我比较幼稚的一个想法。在服务端可以建立一个暗号(Command)sql相对应的一个表(可以用数组实现),这样当客户端有请求的时候可以根据这个暗号来查询SQL。在一定程度上避免了恶意的SQL输入。
其实FDO不仅仅是Flash(Client)中的FDO,她应当还要包括服务端的FDO,我这里使用了ASP,用服务端的FDO类来封装ADO,所以在服务端几乎无须涉及ADO只要调用用FDO方法或过程就可以了。
那就先来说说 Flash(Client)的问题,客户端FDO可以位于FDO包中可以包含以下几个类和一个包含文件:

·包含文件:声明作者和版本信息,以及公用函数等。

·FDO.as:这个文件中定义FDO类,完成与服务器的数据交互。

·Command.as:用于生成操作命令,并提交到FDO类。

·RecordSet.as:使非常容易的获取接收到的XML中的数据,类似ADO中的RecordSet对象。它被设计成只读。

·Record.as:定义Record类,这个类主要是在更新和插入数据时作为参数输入。

在这里,我把RecordSetlei类设计为只读,我觉得修改它的数据没有任何意义,因为它不会主动更新数据库。其最主要的原因就是网络延时,任何一次与服务器的交互都需要等待时间。当我们把程序的主动权交给Flash Player之后(Flash Player要等待服务器反馈,然后调用onData onLoad之类的事件),只有当Player调用数据下载完成的事件以后,Flash的继续播放才是有意义的,原因很简单:如果你不给我数据,我怎么往前走!所以既然FDO类是直接与服务器交互的,那么就应该为FDO定义回调的事件,以供FDO用户重写这些事件。这些事件的目的就是:等待数据或操作(即与服务器的交互)完成后,继续驱动Flash的播放。这一点,很好理解,我们通常都会用一个下载过程在片头,等下载完成后在往后面播放。道理是一样的,没有服务器数据,播放很有可能出错,所以Flash要等到服务器数据到达以后才把主动权叫交给用户(指开发人员),用户可以重写回调函数。

与服务器的交互通常包括四种:查询、插入、更新、删除。只有查询才返回用户(开发人员)所需要的数据,其他的操作返回一个操作结果就可以了,比如说,更新了几条记录,插入数据是否成功,或者在操作过程中发生了一些不可以预测的错误,这些错误都应该返回给Flash客户端,以便开发者进一不处理,或者把错误告之应用的最终用户。这些都保证了程序的健壮性。

那么FDO类到底要如何设计呢? 我的做法是首先是两个XML对象,一个用于发送命令给服务端,一个则接收服务端返回的数据,当然这些数据、命令、结果都是XML格式的。然后还需要维护FDO状态,是空闲,还是繁忙等。FDO还应该包括三个事件供用户重写,以驱动他们的Flash继续播放:

·onLoading 事件:该时间输入一个加载的百分比作为参数,并被持续调用直到FDO与服务器的交互完成,就像onEnterFrame一样,当然调用的频度可以由FDO的设计者决定,也可以用户作为参数输入。这个事件的目的是驱动下载进度条,为最终用户提供良好的交互。

·onData事件:该事件通常在执行查询后数据下载完成时被调用,传入从服务器获取的XML数据(也就是 XML.onLoad事件中的this引用),在RecordSet类构造时必须传如这个参数。
·onResult事件:在执行插入、更新或修改后调用该事件,告诉开发这者命令执行是否成功。
FDO类还需要一个持续调用的方法来检查下载进度,在这个方法中再调用onLoading函数。
Command类主要任务是把命令和参数以一定的格式生成 XML,然后交给FDO。至于RecordSet类和Record类都是比较简单。等一下我贴部分源代码出来。

服务端的FDO类相对简单,主要用到ADO以及 XMLDOM 大家可以参看网上的一些资料。
FDO 部分代码:
FDO.as 代码:

class FDO
{
//版本和作者信息
#include "FDO_include.as"
//指定FDO_SERVER文件夹所在的目录
private var sev_url:String;
//指定FDO_SERVER的名称,默认名称为:FDO_SERVER
private var sev_name:String;
//connection 状态描述,一个connection 实例只能和一个command实例绑定
//-1:connection创建错误。
//0:空闲且未与command绑定
//1:空闲但已经绑定到command
//2: 正在与服务器进行通信
public var state:Number = -1;
private var rst_XML:XML;
private var error:String;
private var intervalID:Number;
private var percent:Number;
private var pct:Function;
//为Connection 添加两个事件
public var onLoading:Function;
public var onData:Function;
public var onResult:Function;
//构造函数要求传入FDO服务器所在的url位置和FDO服务器的名称
function FDO (url:String, name:String)
{
if (url == null or url == undefined)
{
throw new Error ("Error:Connection.Constructor url required.");
return;
}
if (name == null or name == undefined)
{
throw new Error ("Error:Connection.Constructor name required.");
return;
}
System.useCodepage = true;
var me:FDO = this;
error = "";
percent = 0;
intervalID = 0;
state = 0;
sev_url = url;
sev_name = name;
rst_XML.ignoreWhite = true;
rst_XML = new XML ();
rst_XML.onLoad = onXML;
function onXML (suc)
{
var err_XML:XML;
var root:XMLNode;
clearInterval (me.intervalID);
delete me.intervalID;
if (suc)
{
me.percent = 100;
me.state = 1;
//当数据下载完成后最后调用一次
err_XML = this;
root = err_XML.firstChild;
//trace ("root.nodeName=" + root.nodeName + (root.nodeName == "err"));
if (root.nodeName == "err")
{
me.error = "ServerError:[type:" + root.attributes.type + ";info:" + root.attributes.info + ";discription:" + root.attributes.discription + "]";
me.onLoading (me.percent);
throw new Error (me.error);
return;
}
else if (root.nodeName == "result")
{
me.error ="";
me.onLoading (me.percent);
me.onResult(this.firstChild.attributes.info,me.state);
return;
}
else
{
//获取服务端返回的请求数据
trace ("Success[FDO]:ServerData Loaded....");
//当数据被成功加载后,调用FDO的onData方法,在外部可以重写该事件
rst_XML = this;
//me.state = 1;
me.error = "";
//置Connection状态为 数据等待被传递
me.onLoading (me.percent);
me.onData (this, me.state);
}
}
else
{
me.error = "NetworkError:[Network or ServerError,please check the Network]";
me.onLoading (0);
throw new Error (me.error);
}
}
//检查进度
function checkProgress (XMLObj:XML)
{
var bytesLoaded:Number = XMLObj.getBytesLoaded ();
var bytesTotal:Number = XMLObj.getBytesTotal ();
me.percent = Math.floor ((bytesLoaded / bytesTotal) * 100);
if (isNaN (me.percent))
{
me.percent = 0;
}
//持续调用
me.onLoading (me.percent);
if (me.percent >= 100)
{
clearInterval (me.intervalID);
delete me.intervalID;
me.state = 1;
}
}
//checkProcess 引用
pct = checkProgress;
//修改XML模型,使抛出异常
XML.prototype.onData = function (src:String)
{
try
{
if (src == undefined)
{
this.onLoad (false);
}
else
{
this.parseXML (src);
this.loaded = true;
this.onLoad (true);
}
}
catch (ex)
{
trace (ex);
}
};
}
//与服务端通信,执行数据库命令,并返回结果。
//参数:eData:发送到服务端的数据;eType:执行的操作类型。
public function execute (cmd_XML:XML)
{
if (state != 1)
{
throw new Error ("Error FDO.execute Connection is busy or Connection Creation error.state=" + state);
return null;
}
if (cmd_XML == null or cmd_XML == undefined)
{
throw new Error ("Error:FDO.execute no command.");
return null;
}
//锁定Connection 到通信状态
state = 2;
//清零加载量和错误信息
percent = 0;
error = "";
var url:String = cmd_XML.firstChild.attributes.url;
intervalID = setInterval (pct, 50, rst_XML);
//rst_XML;
cmd_XML.sendAndLoad (sev_url + "/" + sev_name + "/FDO/" + url + "?LeeFJ=Leaf", rst_XML);
return null;
}
}
因为我觉得FDO类设计比较难,也是核心,所以我把它贴出来大家看看,至于其他的类,就相对比较简单了。
在测试fla文件的第一帧代码:
var myfdo:FDO = new FDO ("http://10.1.51.82:88/FDO", "FDO_SERVER");
//var myfdo:FDO = new FDO ("http://127.0.0.1/FDO", "FDO_SERVER");
var cmd:Command = new Command (myfdo);
//cmd.executeQuery("give me money",);
cmd.Query ("get top 150", ["0"],"query.ASP");
var rs:RecordSet;
//重写FDO的 onData事件
myfdo.onData = function (it, state)
{
//trace(it);
rs = new RecordSet (it);
//trace ("len=" + rs.len ());
//trace ("line no="+rs.getLine ());
//trace ("islast="+rs.isLast ());
//trace ("isfirst="+rs.isFirst ());
//
//rs.moveLast();
//rs.moveFirst();
//rs.moveLine(5);
//trace(rs.get("name"));
t_txt.text += "Data Loaded..." + state;
//cmd.Addnew("adduser",rcd,"addnew.ASP")
//cmd.Update("update password",rcd2,["LeeFJ"],"update.ASP")
cmd.Delete ("delete by name", ["LeeFJ"],"delete.ASP");
};
//重写FDO的 onLoading 事件
myfdo.onLoading = function (pct)
{
t_txt.text += pct + "%,";
};

myfdo.onResult=function(info,state)
{
trace("Result:"+info);
};


var rcd:Record=new Record();
rcd.set("name","LeeFJ");
rcd.set("password","1234567");
rcd.set("email","tianmazhixing@eyou.com");
rcd.set("qq","123456789");
rcd.set("role","秘密");

rcd.set("sex","boy");
//rcd.set("age","19");
rcd.set("XML","我是一个胆小鬼!");

var rcd2:Record=new Record();
rcd2.set("password","666666");
rcd2.set("role","流氓");

广告位