ActionScript簡單實現Socket Tcp應用協議分析器

來源:互聯網
上載者:User

    其實Flash上做通訊很多情況都選擇AMF,畢竟他是AS內部基於對象進位序列協議,容量小效率高。但有時為了去調用一些已經有的Tcp服務,而這些服務並不是提供AMF支援;這時你就不得不實現一個協議的分析。其實AS提ByteArray提供了很多write和read方法,這樣使我們應用起來非常方便。以下是用AS簡單封裝基於訊息頭描述大小的協議分析器。

    為了更好地管理訊息,通過一介面來制寫訊息寫入和讀取規範。

package Beetle.AS{import flash.utils.ByteArray;public interface IMessage{function Load(data:Reader):void;function Save(data:Writer):void;}}

  介面比較簡單分別是保讓到流中和從流中擷取,其中Reader和Writer都派生於ByteArray對象;為什麼沒有直接用ByteArray呢?其原因就可以自己實現更進階的資訊擷取方法.

package Beetle.AS{import flash.net.getClassByAlias;import flash.utils.ByteArray;import flash.utils.getDefinitionByName;import mx.controls.Image;public class Reader extends ByteArray{public function Reader(){super();}public function ReadMessages(className:String):Vector.<IMessage>{var results:Vector.<IMessage> = new Vector.<IMessage>();for(var i:int=0;i<readInt();i++){var msg:IMessage = IMessage(getDefinitionByName(className));results.push(msg);}return results;}}}package Beetle.AS{import flash.utils.ByteArray;public class Writer extends ByteArray{public function Writer(){super();}public function WriteMessages(items:Vector.<IMessage>):void{writeInt(items.length);for each(var item:IMessage in items){item.Save(this);}}}}

  基礎規則都構建好了,下面就開始做協議分析部分。

package Beetle.AS{import flash.net.Socket;import flash.utils.ByteArray;import flash.utils.Endian;import mx.graphics.shaderClasses.ExclusionShader;public  class HeadSizeOfPackage{public function HeadSizeOfPackage(){mWriter.endian = Endian.LITTLE_ENDIAN;mReader.endian = Endian.LITTLE_ENDIAN;}private var mMessageReceive:Function;//訊息接收回呼函數public function get MessageReceive():Function{return mMessageReceive;}public function set MessageReceive(value:Function):void{mMessageReceive = value;}//寫入訊息類型標識protected  function WriteMessageTag(message:IMessage,data:Writer):void{    throw new Error("WriteMessageTag not implement!");}//擷取訊息對象protected  function GetMessageByTag(data:Reader):IMessage{throw new Error("GetMessageByTag not implement!");}private var mReader:Reader = new Reader();private var mSize:int=0;//匯入當前Socket接收的資料public function Import(socket:Socket):void{socket.endian = Endian.LITTLE_ENDIAN;            while(socket.bytesAvailable>0){if(mSize==0){mSize= socket.readInt()-4;mReader.clear();}if(socket.bytesAvailable>= mSize){   socket.readBytes(mReader,mReader.length,mSize);   var msg:IMessage = GetMessageByTag(mReader);   msg.Load(mReader);   if(MessageReceive!=null)   MessageReceive(msg);   mSize=0;}else{mSize= mSize-socket.bytesAvailable;socket.readBytes(mReader,mReader.length,socket.bytesAvailable);}}}private var mWriter:Writer = new Writer();//發磅封裝的協議資料public function Send(message:IMessage,socket:Socket):void{     socket.endian = Endian.LITTLE_ENDIAN; mWriter.clear(); WriteMessageTag(message,mWriter); message.Save(mWriter); socket.writeInt(mWriter.length+4); socket.writeBytes(mWriter,0,mWriter.length); socket.flush();}}}

  協議分析器的實現比較簡單,基礎功能有訊息封裝,對流進行分析還來源物件並把訊息回調到指定的函數中.MessageReceive是一個指向函數的屬性,用於描述訊息接收工作其原理類似於C#的委託。分析器中還有兩個方法需要衍生類別重寫WriteMessageTag和GetMessageByTag,其主要作用是寫入訊息類型標記和根據讀取的標記資訊建立相關聯的對象。

     Send方法是一個協議封裝過程,主要把對象寫入流的資訊加工後用指定的Socket對象發送出去。

public function Send(message:IMessage,socket:Socket):void{     socket.endian = Endian.LITTLE_ENDIAN; mWriter.clear(); WriteMessageTag(message,mWriter); message.Save(mWriter); socket.writeInt(mWriter.length+4); socket.writeBytes(mWriter,0,mWriter.length); socket.flush();}

  工作原理是通過訊息介面的Save方法把對象資訊寫入到流中,第一步是先寫入訊息類型標記具體寫入方法由衍生類別來確用string或int都可以,然後再寫入訊息內容;最後計算所有資料長度的頭寫入到socket再寫入資訊流即可。

    Import方法是一個資料匯入工作,主要負責從Socket中讀取資料進行載入分析。

public function Import(socket:Socket):void{socket.endian = Endian.LITTLE_ENDIAN;            while(socket.bytesAvailable>0){if(mSize==0){mSize= socket.readInt()-4;mReader.clear();}if(socket.bytesAvailable>= mSize){   socket.readBytes(mReader,mReader.length,mSize);   var msg:IMessage = GetMessageByTag(mReader);   msg.Load(mReader);   if(MessageReceive!=null)   MessageReceive(msg);   mSize=0;}else{mSize= mSize-socket.bytesAvailable;socket.readBytes(mReader,mReader.length,socket.bytesAvailable);}}}

  原理很簡單如果當前需要載入的資料為零,則表示為一個表新的訊息;讀取該訊息需要載入的資料的長度,然後從Socket讀取資料寫入到流中,值到讀取的長度和當前訊息長度一致的情況就載入訊息,並通過回呼函數把訊息回調到具體的工作方法中.

     到這裡一個以頭4位元組描述的訊息分析器就完成,直接下來就是使用這個分析器。派生出一個新的分析器,並根據實際的需要實現對訊息標記的處理.

public class HeadSizePackage extends HeadSizeOfPackage{public function HeadSizePackage(){super();}override protected function GetMessageByTag(data:Reader):IMessage {var name:String = data.readUTF();switch(name){case "Register":return new Register();case "User":return new User();case "GetUser":return new GetUser();default :return null;}}override protected function WriteMessageTag(message:IMessage, data:Writer):void{ if(message is Register) { data.writeUTF("Register"); } else if(message is User) { data.writeUTF("User"); } else if(message is GetUser){ data.writeUTF("GetUser"); } else { data.writeUTF("NULL"); }}}

  對於訊息對象的實現也很簡單,只要實現IMessage介面即可

public class Register implements IMessage{public function Register(){}public var UserName:String;public var EMail:Stringpublic function Load(data:Reader):void{UserName= data.readUTF();EMail = data.readUTF();}public function Save(data:Writer):void{data.writeUTF(UserName);data.writeUTF(EMail);}}

  發送這個訊息也比較簡單

var reg:Register= new Register();reg.UserName = txtUserName.text;reg.EMail = txtEmail.text;mPackage.Send(reg,mSocket);

  在使用AS的Socket時發現其實挺方便,很多基礎的方法socket提供,即使是ByteArray也提供這些基礎而又貼心的方法。

 

     

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.