轉自。。smark http://www.cnblogs.com/smark/archive/2012/05/15/2501507.html ActionScript簡單實現Socket Tcp應用協議分析器 其實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:String public 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也提供這些基礎而又貼心的方法。