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也提供这些基础而又贴心的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值