ActionScript 3是Adobe公司开发的用于编写Flash的脚本语言。Adobe新推出的Adobe Flex的Rich Internet Application开发平台同样支持Action Script。ActionScript编写的Flex Data Service提供了丰富的数据处理功能,也包括实现了通过建立HTTPChannel的数据实时更新功能,例如聊天室,股市行情等。本文将使用ActionScript 3.0编写HTTPTunnel Client取代Flex Data Service的HTTPChannel, 用开源的Java HTTPTunnel作为Server,实现数据实时更新。
1 架构
<group id="_x0000_s1026" style="WIDTH: 252.1pt; HEIGHT: 161.95pt; mso-position-horizontal-relative: char; mso-position-vertical-relative: line" coordsize="4291,2776" coordorigin="2279,5367" editas="canvas"><lock aspectratio="t" v:ext="edit"></lock><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_s1027" style="LEFT: 2279px; WIDTH: 4291px; POSITION: absolute; TOP: 5367px; HEIGHT: 2776px" o:preferrelative="f" type="#_x0000_t75"><font size="3"><fill o:detectmouseclick="t"></fill><path o:connecttype="none" o:extrusionok="t"></path><lock v:ext="edit" text="t"></lock></font></shape><shapetype id="_x0000_t202" path="m,l,21600r21600,l21600,xe" o:spt="202" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><path o:connecttype="rect" gradientshapeok="t"></path></shapetype><shape id="_x0000_s1028" style="LEFT: 2739px; WIDTH: 1225px; POSITION: absolute; TOP: 6446px; HEIGHT: 771px" type="#_x0000_t202"><textbox inset="5.85pt,.7pt,5.85pt,.7pt"><table cellspacing="0" cellpadding="0" width="100%"><tbody><tr> <td style="BORDER-RIGHT: #ece9d8; BORDER-TOP: #ece9d8; BORDER-LEFT: #ece9d8; BORDER-BOTTOM: #ece9d8; BACKGROUND-COLOR: transparent"> <div> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">Flash<p></p></font></span></p> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">Web Browser</font></span></p> </div> </td> </tr></tbody></table></textbox></shape><shape id="_x0000_s1029" style="LEFT: 5343px; WIDTH: 1227px; POSITION: absolute; TOP: 6446px; HEIGHT: 771px" type="#_x0000_t202"><textbox inset="5.85pt,.7pt,5.85pt,.7pt"><table cellspacing="0" cellpadding="0" width="100%"><tbody><tr> <td style="BORDER-RIGHT: #ece9d8; BORDER-TOP: #ece9d8; BORDER-LEFT: #ece9d8; BORDER-BOTTOM: #ece9d8; BACKGROUND-COLOR: transparent"> <div> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">JHTTPTunnel <p></p></font></span></p> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">Server<p></p></font></span></p> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US"><p><font face="Century" size="3"></font></p></span></p> </div> </td> </tr></tbody></table></textbox></shape><shapetype id="_x0000_t38" filled="f" path="m,c@0,0@1,5400@1,10800@1,16200@2,21600,21600,21600e" o:spt="38" coordsize="21600,21600" o:oned="t"><formulas><f eqn="mid #0 0"></f><f eqn="val #0"></f><f eqn="mid #0 21600"></f></formulas><path o:connecttype="none" fillok="f" arrowok="t"></path><handles><h position="#0,center"></h></handles><lock v:ext="edit" shapetype="t"></lock></shapetype><shape id="_x0000_s1030" style="LEFT: 4653px; WIDTH: 1px; POSITION: absolute; TOP: 5145px; HEIGHT: 2604px; rotation: 270; flip: y" type="#_x0000_t38" adj="-7776000,-46412,63979200" o:connectortype="curved"><stroke endarrow="block"><font face="Century" size="3"></font></stroke></shape><shape id="_x0000_s1031" style="LEFT: 4653px; WIDTH: 1px; POSITION: absolute; TOP: 5916px; HEIGHT: 2604px; rotation: 90" type="#_x0000_t38" adj="7776000,-52765,-130075200" o:connectortype="curved"><stroke endarrow="block"><font face="Century" size="3"></font></stroke></shape><shape id="_x0000_s1032" style="LEFT: 3658px; WIDTH: 2604px; POSITION: absolute; TOP: 7680px; HEIGHT: 309px" stroked="f" filled="f" type="#_x0000_t202"><textbox inset="5.85pt,.7pt,5.85pt,.7pt"><table cellspacing="0" cellpadding="0" width="100%"><tbody><tr> <td style="BORDER-RIGHT: #ece9d8; BORDER-TOP: #ece9d8; BORDER-LEFT: #ece9d8; BORDER-BOTTOM: #ece9d8; BACKGROUND-COLOR: transparent"> <div> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">3 HTTPHeader+Content Data</font></span></p> </div> </td> </tr></tbody></table></textbox></shape><shape id="_x0000_s1033" style="LEFT: 3964px; WIDTH: 1226px; POSITION: absolute; TOP: 5367px; HEIGHT: 617px" stroked="f" filled="f" type="#_x0000_t202"><textbox inset="5.85pt,.7pt,5.85pt,.7pt"><table cellspacing="0" cellpadding="0" width="100%"><tbody><tr> <td style="BORDER-RIGHT: #ece9d8; BORDER-TOP: #ece9d8; BORDER-LEFT: #ece9d8; BORDER-BOTTOM: #ece9d8; BACKGROUND-COLOR: transparent"> <div> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">1 Post <p></p></font></span></p> <p class="MsoNormal" style="MARGIN: 0mm 0mm 0pt"><span lang="EN-US" style="FONT-FAMILY: SimSun; mso-fareast-language: ZH-CN"><font size="3">2 Get</font></span></p> </div> </td> </tr></tbody></table></textbox></shape><wrap type="none"></wrap><anchorlock></anchorlock></group>
1. Flash客户端连接HTTP Server并向HTTP Server发送Post命令。
2. Flash客户端连接HTTP Server并向HTTP Server发送Get命令。
3. HTTP Server向Flash不断发送遵循HTTP协议的数据,直到Flash客户端发送Close命令关闭连接。
4.Flash客户端解析接受到的数据并更新界面。
2.实现
2.1 客户端
MXML-类似于XML语言,用于部署Flash界面,可被Flex SDK编译为Flash文件。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="620" height="341"
creationComplete="Refresh()">
<mx:Script>
<![CDATA[
import org.colimas.http.*;
import mx.collections.*;
[Bindable]
public var initDG:ArrayCollection;
var host:String="localhost";
var hport:int=8888;
var jhtc:HttpTunnelClient=new HttpTunnelClient(host, hport);
private var DGArray:Array = [
{ corp:'IBM', last:79.99},
{ corp:'MS', last:30.99}];
private function Refresh():void {
trace("start...");
Data1.text="Started";
Data2.text="Yes!";
jhtc.setInBound(new InTunnelSocket());
jhtc.setOutBound(new OutTunnelSocket());
initDG=new ArrayCollection(DGArray);
jhtc.connect();
jhtc.Register(initDG);
jhtc.close();
var myTimer:Timer=new Timer(300);
myTimer.addEventListener("timer", timerHandler);
myTimer.start();
}
public function timerHandler(event:TimerEvent):void {
initDG.refresh();
}
]]>
</mx:Script>
<mx:Text x="41" y="38" text="Text1" width="62" height="28" id="Data1"/>
<mx:Text x="124" y="38" text="Text2" width="62" height="28" id="Data2"/>
<mx:DataGrid x="39" y="86" width="542" editable="false" id="Stock" dataProvider="{initDG}">
<mx:columns>
<mx:DataGridColumn headerText="Corp." dataField="corp"/>
<mx:DataGridColumn headerText="Last" dataField="last"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
界面显示如下:
<shape id="_x0000_i1026" style="WIDTH: 4in; HEIGHT: 239.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image002.png"></imagedata></shape>
Refresh()函数实现数据刷新。org.colimas.http.HttpTunnelClient类用ActionScript语言编写,实现HTTPTunnel客户端,完成连接HTTPTunnel并接受数据任务。
org.colimas.http.HttpTunnelClient实现:
package org.colimas.http
{
import flash.utils.ByteArray;
import flash.errors.IOError;
import mx.collections.ArrayCollection;
public class HttpTunnelClient extends HttpTunnel
{
static private var CONTENT_LENGTH:int=1024*10;
private var init:Boolean=false;
private var closed:Boolean=false;
private var dest_host:String=null;
private var dest_port:int=0;
private var proxy:Proxy=null;
private var ib:InTunnel=null;
private var ob:OutTunnel=null;
public function HttpTunnelClient( host:String, port:int){
this.dest_host=host;
this.dest_port=port;
}
/*传入用于界面显示的数据源*/
public function Register(DGArray:ArrayCollection):void{
this.ib.setData(DGArray);
}
public function setProxy( host:String, port:int):void{
this.proxy=new Proxy(host, port);
}
public function connect():void{
if(ib==null){
trace("InTunnel is not given");
return;
}
ib.setHost(dest_host);
ib.setPort(dest_port);
ib.setProxy(proxy);
if(ob==null){
trace("OutTunnel is not given");
return;
}
ob.setHost(dest_host);
ob.setPort(dest_port);
ob.setProxy(proxy);
ob.setContentLength(CONTENT_LENGTH);
getOutbound();
getInbound();
}
/*数据发送OutTunnel类连接服务器端*/
private function getOutbound():void{
if(closed){
trace("broken pipe");
return;
}
ob.connect();
}
/*数据接受InTunnel连接服务器端*/
private function getInbound():void{
ib.connect();
}
var buf_len:int=0;
public function close():void{
sendClose()
ib.close()
ob.close()
closed=true;
}
public function setInBound( ib:InTunnel):void { this.ib=ib; }
public function setOutBound( ob:OutTunnel):void{ this.ob=ob; }
}
}
数据发送OutTunnel类的实现
package org.colimas.http
{
import flash.net.Socket;
import flash.utils.ByteArray;
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.ErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
public class OutTunnelSocket extends OutTunnel
{
static private var _rn:String="\r\n";
private var socket:Socket=null;
private var request:String="/index.html?crap=1 HTTP/1.1";
public function OutTunnelSocket(){
super();
}
private function errorHandler(event:ErrorEvent):void
{
trace("[" + event.type + "] " + event.toString());
}
private function ioErrorHandler(event:IOErrorEvent):void
{
trace("[" + event.type + "] " + event.toString());
}
/*连接后发送POST请求*/
private function connectHandler(event:Event):void {
trace("Out[" + event.type + "] " + event.toString());
socket.writeUTF(request);
socket.writeUTF(_rn);
socket.writeUTF("Content-Length: "+getContentLength());
socket.writeUTF(_rn);
socket.writeUTF("Connection: close");
socket.writeUTF(_rn);
socket.writeUTF("Host: "+getHost()+":"+getPort());
socket.writeUTF(_rn);
socket.writeUTF(_rn);
socket.flush();
sendCount=getContentLength();
socket.writeByte(HttpTunnel.TUNNEL_OPEN);
socket.writeByte(0);
socket.writeByte(1);
socket.writeByte(0);
}
public override function connect():void{
close();
var host:String=getHost();
var port:int=getPort();
var p:Proxy=getProxy();
if(p==null){
socket=new Socket(host, port);
request="POST "+request;
}
else{
var phost:String=p.getHost();
var pport:int=p.getPort();
socket=new Socket(phost, pport);
request="POST http://"+host+":"+port+request;
}
socket.addEventListener(Event.CONNECT, connectHandler);
socket.addEventListener(ErrorEvent.ERROR, errorHandler);
socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
/*用于向Server发送命令*/
public override function sendData(foo:ByteArray, s:int, l:int, flush:Boolean):void{
if(l<=0) return;
if(sendCount<=0){
trace("1#");
connect();
</sp