AS3: Socket 數據包 收 發


AS3.0中使用Socket使用tcp服務器協議,它是一種流協議,不停的將分片傳輸給客戶端,P作為流,發包是不會整包到達的,而是源源不斷的。
它不同於UDP服務器協議,UDP作為數據包協議,整包到達。   
  
如果要使用Socket接收數據我們必須使用ProgressEvent.SOCKET_DATA事件。這個事件在幫助文檔中是這樣描述的 ——在套接字接收到數據后調度。 
而事實卻並非如此,做過一次嘗試,服務器發送了20000次數據而rogressEvent.SOCKET_DATA事件只產生了2000多次。 
那么為什么說"服務器發送了20000次數據而rogressEvent.SOCKET_DATA事件只產生了2000多次", 
因為flash socket使用的TCP/IP協議, 這個協議跟UDP不同,它不是以單個"包"的形式發送數據,它發送的是"流數據",所以即便你發來20000次數據(也就是你所想象的20000個包),TCP協議也是將它視作"流"發送. 
換句話說,你的20000次數據,實際上只被分割成了2000多個"包"來發送,因此socket收到了2000多個包,,因此只產生了2000多次的事件.
     另外,如果as3 的data事件函數正在執行的時候,比如在此函數中用while循環解碼,此時有新的數據發送過來,data事件還會觸發么?觸發的話,正在執行的怎么辦?原有數據還有么?
答案是會觸發的,所以將socket數據read的時候,必須做一個循環 while,每到一個包剛好讀取完成的時候(包頭用一個整型記錄完整包的長度。每次都先讀取一個包長度,然后按照包長度讀取指定長度的數據作為一個完整數 據包傳遞到到邏輯層),又繼續讀取下一個包,然后把解碼后的每個包都放進一個數組里面依次讀取。還有一點要注意的是 socket.bytesAvailable長度是每read一次就減去所讀的長度,直至讀取完畢,最后為0;此處的bytesAvailable如果重 新設置position為0,那該數組的bytesAvailable又是滿的。
附一下代碼進行研究:
view plaincopy to clipboardprint?
private function Net_Data(evt:ProgressEvent):void  
  {  
       var ba:ByteArray = new ByteArray();//創建一個  
       socket.readBytes(ba, 0, evt.bytesTotal);  //服務器一次性發送的總共的數據,可能是幾個包,也可能是幾個半包  
       packetBuffer.push(ba);   //把ba放入緩沖區,其實就是把ba放入packetBuffer類中的一個ByteArray對象里  
       var packets:Array = packetBuffer.getPackets();  //這里就是在進行解碼(包含循環)  
       for each(var packet:MsgPacket in packets)  
       {  
        dispatch(packet);  //對解碼后的數據進行處理,可以說是直接使用、賦值  
       }  
  }  
private function Net_Data(evt:ProgressEvent):void { var ba:ByteArray = new ByteArray();//創建一個 socket.readBytes(ba, 0, evt.bytesTotal); //服務器一次性發送的總共的數據,可能是幾個包,也可能是幾個半包 packetBuffer.push(ba); //把ba放入緩沖區,其實就是把ba放入packetBuffer類中的一個ByteArray對象里 var packets:Array = packetBuffer.getPackets(); //這里就是在進行解碼(包含循環) for each(var packet:MsgPacket in packets) { dispatch(packet); //對解碼后的數據進行處理,可以說是直接使用、賦值 } }

packetBuffer.as
view plaincopy to clipboardprint?
  package org.green.server.data  
{  
    import flash.utils.ByteArray;  
      
    public class PacketBuffer  
    {  
        private var buf:ByteArray = new ByteArray();  
        private static const SPLIT:int = 21316;// "DS"  
        public function PacketBuffer()  
        {  
        }  
        public function push(ba:ByteArray):void  
        {  
            if(buf == null)  
            {  
                buf = ba;  
            }else  
            {  
                buf.position = buf.length;  
                buf.writeBytes(ba);  
            }  
        }  
        public function getPackets():Array  
        {  
            var ps:Array = [];  
            var ptr:uint = 0;  
            buf.position = ptr;  
            while(buf.bytesAvailable >= 2)  //這里是說當可用數據大於包頭時,一個包==包頭(body的長度)+包體(body),也就是說包里如果一旦有數據就開始執行  
            {                                //2其實是readShort()后,少了的2個字節,也就是body有數據的時候才開始解碼  
                var len:uint = buf.readShort();  
                //不足一個包,這里完全有可能,當只讀取完包頭len,但是body卻沒有讀取到末尾  
                if(buf.bytesAvailable < len)  
                {  
                    var ba:ByteArray = MsgUtil.createByteArray();  
                    buf.position = ptr;  
                    ba.writeBytes(buf, 0, buf.bytesAvailable);            
                    buf = ba;         
                    //返回  
                    return ps;  
                }  
                buf.position = 2;  
                var mb:ByteArray = new ByteArray();  
                buf.readBytes(mb, 0, len);   //len為body的長度,將body的數據放入mb  
                mb.position = 0;  
                var msg:MsgPacket = MsgUtil.createMsgPacket(mb,magic);//這里在對body解碼過程 略  
                buf.position=0;  
                ps.push(msg);  //放入數組  
                //下一個包  while語句進行下一個循環  
            }  
            if(buf.bytesAvailable <= 0)buf = null;  
            return ps;  
        }  
        public function clear():void  
        {  
            buf=null;  
        }  
    }  
}  

as3 socket test
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.external.ExternalInterface;
    import flash.net.Socket;
    public class receiveData extends Sprite
    {
        public function receiveData()
        {
           trace(ProgressEvent.SOCKET_DATA);
           socket.connect("127.0.0.1", 4300); 
           socket.addEventListener(ProgressEvent.SOCKET_DATA, onServerData,false,0,true);
           socket.addEventListener(Event.CONNECT, connectHandler);
           socket.addEventListener(Event.CLOSE, closeHandler);
           socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
        private var socket:Socket=new Socket();
        private var msg:String = "";
        private function onServerData(event:ProgressEvent):void{
            if(socket.bytesAvailable){
                msg = socket.readUTFBytes(socket.bytesAvailable);
                trace(msg);
                ExternalInterface.call("window.jsFunc", msg);
            }
        }
        private function connectHandler(event:Event):void{
           trace("connected");
        }
        private function closeHandler(event:Event):void{
           trace("closed");
            clearHandler();
        }
        private function ioErrorHandler(event:IOErrorEvent):void{
           //to do
           clearHandler();
        }
        private function clearHandler():void{
            socket.removeEventListener(ProgressEvent.SOCKET_DATA, connectHandler);
            socket.removeEventListener(Event.CONNECT, connectHandler);
            socket.removeEventListener(Event.CLOSE, closeHandler);
            socket.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM