Beetle使用FluorineFx和Flash进行AMF3通讯

    之前的文章已经介绍了Beetle使用ProtoBuf.net进行对象序列化数据传输,这一章主要讲述Beetle如何使用FluorineFx和Flash进行AMF3通讯.其实现原理和使用ProtoBuf.net一样,扩展出一个MessageAdapter即可以.

    MessageAdapter的实现如下:

public  class MessageAdapter:IMessage
    {
        public object Message
        {
            get;
            set;
        }
        public static bool Send(TcpChannel channel,object message )
        {
            MessageAdapter ma = new MessageAdapter();
            ma.Message = message;
            return channel.Send(ma);
        }
        public void Load(BufferReader reader)
        {
            ByteArraySegment segment = ArrayPool.Pop();
            int count = reader.ReadInt32();
            reader.Read(count - 4, segment);
            using (System.IO.MemoryStream steram = new System.IO.MemoryStream(segment.Array, segment.Offset, segment.Count))
            {
                FluorineFx.AMF3.ByteArray ba = new FluorineFx.AMF3.ByteArray(steram);
                ba.ObjectEncoding = FluorineFx.ObjectEncoding.AMF3;
                Message = ba.ReadObject();
            }
            ArrayPool.Push(segment);
        }
        public void Save(BufferWriter writer)
        {
            ByteArraySegment segment = ArrayPool.Pop();
            using (System.IO.MemoryStream steram = new System.IO.MemoryStream(segment.Array))
            {
                FluorineFx.AMF3.ByteArray ba = new FluorineFx.AMF3.ByteArray(steram);
                ba.ObjectEncoding = FluorineFx.ObjectEncoding.AMF3;
                ba.WriteObject(Message);
                segment.SetInfo(0, (int)steram.Position);
            }
            writer.Write(segment.Count + 4);
            writer.Write(segment.Array,segment.Offset,segment.Count);
            ArrayPool.Push(segment);
        }
        public static ByteArrayPool ArrayPool = new ByteArrayPool(200, 1024 * 8);
    }

     消息适配器实现比较简单在对象写入流的时候先把AMF3序列化对象流的长度+4写入头4个字节,然后再写入AMF3的数据流内容,从流中读取对象原来一样先把消息长度读取出来然后再读取AMF3数据流然后反序列化对象即可.

     实现一个消息头描述长度的协议分析器:

public class HeadSizePackage:HeadSizeOfPackage
    {
        public HeadSizePackage()
        {
        }
        public HeadSizePackage(TcpChannel channel)
            : base(channel)
        {
        }
        protected override IMessage ReadMessageByType(BufferReader reader, out object typeTag)
        {
            typeTag = "MessageAdapter";
            return new MessageAdapter();
        }
        protected override void WriteMessageType(IMessage msg, BufferWriter writer)
        {
            
        }
        public override void MessageWrite(IMessage msg, BufferWriter writer)
        {
            msg.Save(writer);
        }
        public override IMessage MessageRead(BufferReader reader)
        {
            IMessage msg = null;
            object typeTag;
            msg = ReadMessageByType(reader, out typeTag);
            if (msg == null)
                throw NetTcpException.TypeNotFound(typeTag.ToString());
            try
            {
                msg.Load(reader);
            }
            catch (Exception e)
            {
                NetTcpException err = NetTcpException.ObjectLoadError(typeTag.ToString(), e);
                throw err;
            }
            return msg;
        }
    }

     这样一个消息扩展就完成具体生成的协议格式如下:

    协议制定后就可似使用Beetle搭建基于AMF3的.net和flash数据传输。

首先是制定一个Tcp服务

            TcpUtils.Setup(200, 1, 1);
            TcpServer server = new TcpServer();
            server.ChannelConnected += OnConnected;
            server.ChannelDisposed += OnDisposed;
            server.Open(8340);

    以上代码很简单初始化组件信息,构建一个TcpServer并绑定连接接入事件和连接断开事件;然后在所有IP的8340端绑定tcp服务。在连接接入的时候我们需要做些事情。

        static void OnConnected(object sender, ChannelEventArgs e)
        {
            e.Channel.SetPackage<Beetle.FluorineFxAdapter.HeadSizePackag>().ReceiveMessage = OnMessageReceive;
            e.Channel.ChannelError += OnError;
            e.Channel.BeginReceive();
            Console.WriteLine("{0} connected!", e.Channel.EndPoint);
        }

    在接入的事件里针对当前的Tcp通道设置一个协议分包器,并指定对应接收消息事件;Tcp通道相关信息设置完成后就可以调用BeginReceive()方法进入数据接收状态。接下来是消息处理事件的代码:

         static void OnMessageReceive(PacketRecieveMessagerArgs e)
        {
            Beetle.FluorineFxAdapter.MessageAdapter adapter = (Beetle.FluorineFxAdapter.MessageAdapter)e.Message;
            if (adapter.Message is AMF3.Messages.Register)
            {
                OnRegister((AMF3.Messages.Register)adapter.Message,e.Channel);
            }
            else if (adapter.Message is AMF3.Messages.Get)
            {
                OnGet((AMF3.Messages.Get)adapter.Message, e.Channel);
            }
            else
            {
            }
            
        }
        static void OnRegister(AMF3.Messages.Register e, TcpChannel channel)
        {
            Console.WriteLine("{0} Register\t UserName:{1};PWD:{2};EMail:{3}", channel.EndPoint, e.UserName, e.PWD, e.EMail);
        }
        static void OnGet(AMF3.Messages.Get e, TcpChannel channel)
        {
            Console.WriteLine("{0} Get \t Customer:{1}", channel.EndPoint, e.CustomerID);
            AMF3.Messages.GetResponse response = new Messages.GetResponse();
            for (int i = 0; i < 10; i++)
            {
                AMF3.Messages.Order order = new Messages.Order();
                order.OrderID = 10248;
                order.CustomerID = "WILMK";
                order.EmployeeID = 5;
                order.OrderDate = 629720352000000000;
                order.RequiredDate = 629744544000000000;
                order.ShipAddress = "59 rue de l'Abbaye";
                order.ShipCity = "Reims";
                order.ShipCountry = "France";
                order.ShipName = "Vins et alcools Chevalier";
                order.ShipPostalCode = "51100";
                order.ShipRegion = "RJ";
                response.Items.Add(order);
            }
            Beetle.FluorineFxAdapter.MessageAdapter.Send(channel, response);
        }

    在这个例子中只处理了两种消息对象,分别是Register和Get;接收到Register只做了一个简单的输出,而在接收到Get则会返回一个Order列表。

Flash端实现

    首先要实现协议分包器,由于Flash提供的Socket方法挺方便所以实现起来也是很容易的事情.

package
{
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	import mx.graphics.shaderClasses.ExclusionShader;
	public  class HeadSizeOfPackage
	{
		public function HeadSizeOfPackage()
		{

		}
		private var mMessageReceive:Function;
		//消息接收回调函数
		public function get MessageReceive():Function
		{
			return mMessageReceive;
		}
		public function set MessageReceive(value:Function):void
		{
			mMessageReceive = value;
		}
		private var mReader:ByteArray = new ByteArray();
		private var mWriter:ByteArray = new ByteArray();
		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:Object = mReader.readObject();
					if(MessageReceive!=null)
						MessageReceive(msg);
					mSize=0;
				}
				else{
					mSize= mSize-socket.bytesAvailable;
					socket.readBytes(mReader,mReader.length,socket.bytesAvailable);
				}
			}
		}
		//发磅封装的协议数据
		public function Send(message:Object,socket:Socket):void
		{
			socket.endian = Endian.LITTLE_ENDIAN;
			mWriter.clear();
			mWriter.writeObject(message);
			socket.writeInt(mWriter.length+4);
			socket.writeBytes(mWriter,0,mWriter.length);
			socket.flush();
		}
	}
}

    如果需要读取的数据大小为零则表明是一个新的消息,这个时候先把消息大小读取出来,注意由于c#是低字序,beetle的实现也没有处理.所以在这里需要把flash的socket设置成低字序处理.当读取一个完整的AMF3数据流后就直接读取相关对象并通过函数回调.在发送消息的方法原理一样,先写入消息总长度然后写入对应的AMF3数据流即可.这样一个flash端的分包和封包器就完成,下面就可以接入到.net的服务端进行数据交互了.

private var mPackage:HeadSizeOfPackage = new HeadSizeOfPackage();
			protected function cmdConnect_clickHandler(event:MouseEvent):void
			{
				// TODO Auto-generated method stub
				mSocket = new Socket();
				mSocket.connect(txtIPAddress.text,9860);
				mSocket.addEventListener(Event.CONNECT,onConnected);
				mSocket.addEventListener(ProgressEvent.SOCKET_DATA,socketDataHandler);
				mSocket.endian = Endian.LITTLE_ENDIAN;
				mPackage.MessageReceive=OnReceive;
				
			}
			private function OnReceive(msg:Object):void
			{
				if(msg is GetResponse)
				{
					var response:GetResponse=  GetResponse(msg);
					lstData.dataProvider=new ArrayCollection(response.Items);
				}
			}
			private function socketDataHandler(event:ProgressEvent):void {
				trace("socketDataHandler: " + event);
				mPackage.Import(mSocket);
				
			}
			private function onConnected(event:Event):void
			{
				cmdRegister.enabled= true;
				cmdGet.enabled = true;
			}
			
			protected function cmdRegister_clickHandler(event:MouseEvent):void
			{
				// TODO Auto-generated method stub
				var reg:Register = new Register();
				reg.EMail = txtEMail.text;
				reg.UserName=txtUserName.text;
				reg.PWD = txtPWD.text;
				mPackage.Send(reg,mSocket);
			}
			
			protected function cmdGet_clickHandler(event:MouseEvent):void
			{
				// TODO Auto-generated method stub
				var get:Get = new Get();
				get.CustomerID = txtCustomerID.text;
				mPackage.Send(get,mSocket);
			}

    具体运行效果

下载相关代码

posted @ 2012-06-01 09:30  beetlex  阅读(2454)  评论(0编辑  收藏  举报