ActionScript 3Adobe公司开发的用于编写Flash的脚本语言。Adobe新推出的Adobe FlexRich Internet Application开发平台同样支持Action ScriptActionScript编写的Flex Data Service提供了丰富的数据处理功能,也包括实现了通过建立HTTPChannel的数据实时更新功能,例如聊天室,股市行情等。本文将使用ActionScript 3.0编写HTTPTunnel Client取代Flex Data ServiceHTTPChannel 用开源的Java HTTPTunnel作为Server,实现数据实时更新。

 

 

1 架构

 

Flash

 

Web Browser

JHTTPTunnel

 

Server

 

 

3 HTTPHeader+Content Data

1 Post

 

2 Get

 

1. Flash客户端连接HTTP Server并向HTTP Server发送Post命令。

 

2. Flash客户端连接HTTP Server并向HTTP Server发送Get命令。

 

3. HTTP ServerFlash不断发送遵循HTTP协议的数据,直到Flash客户端发送Close命令关闭连接。

 

4Flash客户端解析接受到的数据并更新界面。

 

 ActionScript 3的HTTPTunnel的实现_客户端

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>

 

 

界面显示如下:

 

 

 

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();
            }
        
            var retry:int=2;
            while(retry>0){
              try{
                    socket.writeBytes(foo, s, l);
                    if(flush){ socket.flush();    }
                    sendCount-=l;
                    return;
              }catch(e:Error){
                trace(e.message);
                connect();
                retry--;
              }
              
            }
          }
          public override function close():void{
            if(socket!=null && socket.connected){
              socket.close();
              socket=null;
            }
          }        
    }
}

 

数据发送InTunnel类的实现

 

package org.colimas.http
{
    import flash.utils.ByteArray;
    import flash.net.Socket;
    import flash.events.DataEvent;
    import flash.events.Event;
    import flash.events.ErrorEvent;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import mx.collections.ArrayCollection;
    public class InTunnelSocket extends InTunnel
    {
          static private var  _rn:String="/r/n";
        
          private var socket:Socket=null;
          private var request:String="/index.html?crap=1 HTTP/1.0";
          private var receivedData:String;
          private var ready:int;
          private var DGArray:ArrayCollection;
          
          public function InTunnelSocket(){
                  super();
          }
          
          public override function setData(DGArray:ArrayCollection){
              this.DGArray=DGArray;
          }
          
          private function errorHandler(event:ErrorEvent):void
          {
              trace("[" + event.type + "] " + event.toString());
              
          }
          
          private function ioErrorHandler(event:IOErrorEvent):void
          {
              trace("[" + event.type + "] " + event.toString());
          }

 

          /*连接后发送数据接受请求*/

 

          private function connectHandler(event:Event):void {
             trace("In[" + event.type + "] " + event.toString());    
             ready=0;    
             socket.writeUTFBytes(request);
             socket.writeUTFBytes(_rn);
             socket.flush();    
          }
          /*接受到数据后,解析数据*/

 

          private function dataHandler(event:ProgressEvent):void {
            try{
                

 

                 var tmp:Array=new Array();
                 receivedData=socket.readUTFBytes(socket.bytesAvailable);
                 if(receivedData==null)
                     return;
                 var lines:Array=receivedData.split("/r/n");
                 if(lines.length==0)
                     return;
                 var got:Boolean;
                 got=false;
                 for each (var i:String in lines){
                     if(got==true){
                         var stocks:Array=i.split(" ");
                         if(stocks.length!=2)
                             return;
                         tmp=[
                             {corp:&apos;IBM&apos;,last:stocks[0]},
                             {corp:&apos;MS&apos;,last:stocks[1]}
                         ];
                         this.DGArray.source=tmp;
                         this.DGArray.refresh();
                     }
                     if(i=="Content-Type: text/html; charset=iso-8859-1")
                         got=true;
                 }
             }catch(e:Error)
             {
                 //var tmp:int=socket.readByte();

 

             }
          }
          
          private function closeHandler(event:Event):void
          {
            trace("In[" + event.type + "] " + event.toString());
          }
          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="GET "+request;
            }
            else{
              var phost:String=p.getHost();
              var pport:int=p.getPort();
              socket=new Socket(phost, pport);
              
              request="GET http://"+getHost()+":"+getPort()+request;              
            }
                  socket.addEventListener(Event.CONNECT, connectHandler);
                socket.addEventListener(ErrorEvent.ERROR, errorHandler);
                socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
                socket.addEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
                socket.addEventListener(Event.CLOSE,closeHandler);                
          }
        
          public override function receiveData():String{
                return this.receivedData;
          }
        
          public override function close():void{
            if(socket!=null && socket.connected==true){
              socket.close();
              socket=null;
            }
          }    
          
    }
}

其他

 

package org.colimas.http
{
    public class HttpTunnel
    {
          public static var TUNNEL_OPEN:int=1;
          public static var TUNNEL_DATA:int=2;
          public static var TUNNEL_PADDING:int=3;
          public static var TUNNEL_ERROR:int=4;
          public static var TUNNEL_SIMPLE:int=0x40;
          public static var TUNNEL_PAD1:int=5|TUNNEL_SIMPLE;
          public static var TUNNEL_CLOSE:int=6|TUNNEL_SIMPLE;
          public static var TUNNEL_DISCONNECT:int=7|TUNNEL_SIMPLE;        
    }
}

 

package org.colimas.http
{
    import flash.utils.ByteArray;
    import mx.collections.ArrayCollection;
    
    public class InTunnel extends Tunnel
    {
        public function receiveData():String
        {
            return null;
        }
        public function setData(DGArray:ArrayCollection){
              
        }
    }
}
package org.colimas.http
{
    import flash.utils.ByteArray;
    
    public class OutTunnel extends Tunnel
    {
      static private var CONTENT_LENGTH:int=1024;
      private var content_length:int=CONTENT_LENGTH;
      var sendCount:int;
      public function setContentLength( content_length:int):void{ 
        this.content_length=content_length;
      }
      protected function getContentLength():int{return content_length;}
    
      public function sendData(foo:ByteArray,  s:int,  l:int,  flush:Boolean):void
      {
      
      };        
    }
}
package org.colimas.http
{
    public class Tunnel
    {
          private var host:String=null;;
          private var port:int=8888;
          private var proxy:Proxy=null;
          
          public function setHost(host:String):void
          { 
              this.host=host; 
          }
          public function setPort( port:int):void
          {
              this.port=port; 
          }
          
          public function setProxy( proxy:Proxy):void
          { 
              this.proxy=proxy; 
          }
          
          protected function getHost():String
          {
              return host;
          }
          
          
          protected function getPort():int
          {
              return port;
          }
          
          
          protected function getProxy():Proxy
          {
              return proxy;
          }
        
          public function connect():void
          {
          
          } ;
          public function close():void
          {
              
          } ;
    }
}
package org.colimas.http
{
    public class Proxy
    {
      var host:String;
      var port:int;
      var auth_name:String=null;
      var auth_passwd:String=null;
      function Proxy( host:String,  port:int){
        this.host=host;
        this.port=port;
      }
      function getHost():String{ return host; }
      function getPort():int{ return port; }
      function setAuth( name:String,  passwd:String):void{
        this.auth_name=name;
        this.auth_passwd=passwd;
      }
      function getAuthName():String{return auth_name;}
      function getAuthPasswd():String{return auth_passwd;}        
    }
}

 

3 服务器端

 

服务器端为JHTTPTunnel,可在网上下载。

 

我把部分代码修改,并加入数据生成程序。

 

  void ok(MySocket mysocket, InputStream in, int l, String sid) throws IOException{

 

              //死循环

 

              for(;;){

 

                           

 

                            try {

 

                                          Thread.sleep(300);

 

                                          mysocket.println("HTTP/1.1 200 OK");

 

                                          mysocket.println("Last-Modified: Thu, 04 Oct 2001 14:09:23 GMT");

 

                                          if(sid!=null){

 

                                                        mysocket.println("x-SESSIONID: "+sid);

 

                                          }

 

                                          mysocket.println("Content-Length: "+l);

 

                                          mysocket.println("Connection: close");

 

                                          mysocket.println("Content-Type: text/html; charset=iso-8859-1");

 

              //数据生成                          SimularData.stock[0]=SimularData.simulateChange(SimularData.stock[0]);

 

                                          SimularData.stock[1]=SimularData.simulateChange(SimularData.stock[1]);

 

                                          String tmp=String.valueOf(SimularData.stock[0])+" "+String.valueOf(SimularData.stock[1]);

 

//发送

 

                                          mysocket.println(tmp);

 

                                          mysocket.flush();

 

                            } catch (InterruptedException e) {

 

                                          // TODO Auto-generated catch block

 

                                          e.printStackTrace();

 

                                          mysocket.close();

 

                                          return;

 

                            } catch (IOException e) {

 

                                          // TODO Auto-generated catch block

 

                                          e.printStackTrace();

 

                                          mysocket.close();

 

                                          return;

 

                            }

 

                           

 

              }

 

4.运行结果

 

运行HTTPTunnel Server后,再打开Flash,可以看到界面的数据在不断更新。IBM和微软的股票在不断下降,哈哈。

ActionScript 3的HTTPTunnel的实现_ide_02