• 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);
    			}

        具体运行效果

    下载相关代码

    访问Beetlex的Github
  • 相关阅读:
    动态规划之背包问题
    Python中import导入上一级目录模块及循环import问题的解决
    Anaconda介绍、安装及使用教程
    负载均衡基础知识
    TCP和UDP的区别(转)
    microsoft visual c++ 14.0 is required问题解决办法
    python使用requests时报错requests.exceptions.SSLError: HTTPSConnectionPool
    解决Anaconda无法更新的问题
    彻底的理解TCP协议的三次握手和四次分手
    android调试工具 adb命令学习
  • 原文地址:https://www.cnblogs.com/smark/p/2529832.html
Copyright © 2020-2023  润新知