欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【转载+原创】Flash 接收 Socket 数据包

程序员文章站 2024-01-31 18:09:40
...

最近要写一个c++和flash通过tcp通信的程序, 遇到了不少的麻烦, 最后是解决了, 现把解决方法记录一下

需要解决的问题就是使用C++通过本地tcp连接向flash程序单向循环发送字符串, 遇到的问题就是在flash端接收的时候发现明明在C++端分好几次send的数据, 在flash一端的回调却只会调用一次。

 

转载自:http://cjmxp007.blog.163.com/blog/static/35473837201061054116916/

 

AS3.0 中使用Socket使用 tcp服务器协议,它是一种流协议,不停的将分片传输给客户端,作为流,发包是不会整包到达的,而是源源不断的。

它不同于UDP服务器协议,UDP作为数据包协议,整包到达。   

 

如果要使用Socket接收数据我们必须使用ProgressEvent.SOCKET_DATA 事 件。这个事件在帮助文档中是这样描述的 ——在套接字接收到数据后调度。
而事实却并非如此,做过一次尝试,服务器发送了20000次数据而 rogressEvent.SOCKET_DATA事件只产生了2000多次。
那么为什么说"服务器发送了20000次数据而 rogressEvent.SOCKET_DATA事件只产生了2000多次",
因为flash socket使用的TCP/IP协议, 这个协议跟UDP不同,它不是以单个"包"的形式发送数据,它发送的是"流数据",所以即便你发来20000次数据(也就是你所想象的20000个 包),TCP协议也是将它视作"流"发送.
换句话说,你的20000次数据,实际上只被分割成了2000多个"包"来发送,因此socket收 到了2000多个包,,因此只产生了2000多次的事件.

     另外,如果as3 的data事件函数正在执行的时候,比如在此函数中用while循环解码,此时有新的数据发送过来,data事件还会触发么?触发的话,正在执行的怎么 办?原有数据还有么?

答案是会触发的,所以将socket数据read的时候,必须做一个循环 while,每到一个包刚好读取完成的时候(包头用一个整型记录完整包的长度。每次都先读取一个包长度,然后按照包长度读取指定长度的数据作为一个完整数 据包传递到到逻辑层),又继续读取下一个包,然后把解码后的每个包都放进一个数组里面依次读取。还有一点要注意的是 socket.bytesAvailable长度是每read一次就减去所读的长度,直至读取完毕,最后为0;此处的bytesAvailable如果重 新设置position为0,那该数组的bytesAvailable又是满的。

附一下代码进行研究:

private function Net_Data(evt:ProgressEvent):void
{
       var ba:ByteArray = new ByteArray();//创建一个
       socket.readBytes(ba, 0, evt.bytesTotal);  //服务器一次性发送的总共的数据,可能是几个包,也可能是几个半包
       packetBuffer.push(ba);   //把ba放入缓冲区,其实就是把ba放入packetBuffer类中的一个ByteArray对象里
       var packets:Array = packetBuffer.getPackets();  //这里就是在进行解码(包含循环)
       for each(var packet:MsgPacket in packets)
       {
        dispatch(packet);  //对解码后的数据进行处理,可以说是直接使用、赋值
       }
}

 

packetBuffer.as

 

package org.green.server.data
{
    import flash.utils.ByteArray;
    
    public class PacketBuffer
    {
        private var buf:ByteArray = new ByteArray();
        private static const SPLIT:int = 21316;// "DS"
        public function PacketBuffer()
        {
        }
        public function push(ba:ByteArray):void
        {
            if(buf == null)
            {
                buf = ba;
            }else
            {
                buf.position = buf.length;
                buf.writeBytes(ba);
            }
        }
        public function getPackets():Array
        {
            var ps:Array = [];
            var ptr:uint = 0;
            buf.position = ptr;
            while(buf.bytesAvailable >= 2)  //这里是说当可用数据大于包头时,一个包==包头(body的长度)+包体(body),也就是说包里如果一旦有数据就开始执行
            {                                //2其实是readShort()后,少了的2个字节,也就是body有数据的时候才开始解码
                var len:uint = buf.readShort();
                //不足一个包,这里完全有可能,当只读取完包头len,但是body却没有读取到末尾
                if(buf.bytesAvailable < len)
                {
                    var ba:ByteArray = MsgUtil.createByteArray();
                    buf.position = ptr;
                    ba.writeBytes(buf, 0, buf.bytesAvailable);            
                    buf = ba;        
                    //返回
                    return ps;
                }
                buf.position = 2;
                var mb:ByteArray = new ByteArray();
                buf.readBytes(mb, 0, len);   //len为body的长度,将body的数据放入mb
                mb.position = 0;
                var msg:MsgPacket = MsgUtil.createMsgPacket(mb,magic);//这里在对body解码过程 略
                buf.position=0;
                ps.push(msg);  //放入数组
                //下一个包  while语句进行下一个循环
            }
            if(buf.bytesAvailable <= 0)buf = null;
            return ps;
        }
        public function clear():void
        {
            buf=null;
        }
    }
}

 

 

上文中使用的是一个缓存的技术解决了问题, 但是我不需要这么复杂, 最后, 我是这样解决问题的:

 

package 
{
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.Socket;


	/**
	 * ...
	 * @author 
	 */
	
	public class get_udp extends MovieClip 
	{
		var RFIDSocket:Socket;
		var receive_buffer:String="";
		var comm_splitter:String = "\n";
		public function get_udp() 
		{
			init();
		}
		//var RFIDSocket:Socket;
		function init():void
		{
			RFIDSocket = new Socket("localhost",16000);

			RFIDSocket.addEventListener(ProgressEvent.SOCKET_DATA, socketData);
		}
		private function socketData(e:ProgressEvent):void
		{
            trace("收到的字节数"+RFIDSocket.bytesAvailable);
            while (RFIDSocket.bytesAvailable)  
            {
				// TODO: 进行优化, 一次读入多个字节, 检查是否含有回车, 然后进行截取
                var temp_char:String = RFIDSocket.readUTFBytes(1); 
				// 如果读入的是通信分割, 则进行处理
                if (temp_char == comm_splitter)
				{
					processKinectData(receive_buffer);
					receive_buffer = "";
				}else // 如果读入的是普通字符, 则存储
				{
					receive_buffer += temp_char;
				}
            }  
		}
		private function processKinectData(data:String):void
		{
			trace("处理Kinect数据:" + data);
		}

	}
}
 

 

-------------------------EOF----------------------