梦星痕

--记录生活与工作中的点点滴滴
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【收集】Flash socket在windows下出现的怪异的行为及解决

Posted on 2013-04-25 17:27  梦星痕  阅读(131)  评论(0)    收藏  举报

【原贴地址:http://blog.sina.com.cn/s/blog_538d55be01016uma.html 】

上次就领教过了,在socket的环境下编程,如果服务器端发包非常频繁,Flash会自动把几个包合并为一个
包接收。

所以必须人为的来区别每个包,而不能依赖ProgressEvent.SOCKET_DATA的事件触发。

因为一次事件触发可能包含了你的多个返回包。我的做法是在包前固定长度指明后面的包大小。

一直在MacOS的环境下开发,也没有问题。

今天在windows下准备投产Flash客户端却出问题了。

当服务器返回的包较大时(几十K), 在Mac下一切正常,在windows下客户端的数据却接受不全导致
数据混乱。

一开始我以为是服务端发送的包太大,就把发送改为拆包发送,每个包最大1024个字节。客户端收到
后自己组装。

最后证明不是这个原因。

经过反复查看数据包的记录,终于发现windows下Flash和Mac下的Flash的不同。

我们知道Flash的socket机制会把几个包合并为一个大包,引发一次Progress.SOCKET_DATA事件。

也就是说一次事件可能包含多个服务端发送的数据包。

但是,在windows下还有一种情况,当包特别频繁的到达时,还会把一个数据包分拆开,放到下一个
Progress.SOCKET_DATA里面。

比如:服务端发送了"ab", "cd", "ef"三个包,客户端可能会触发两次事件,socket里面分别包含了"abcde"和
"f"两个数据包。所以,如果只看第一个数据包,就会发现数据没有接受完整。必须等到下一个包到达时,才
能使用第三个数据包。

知道原因,就好办了。我的解决方案是:用一个Buffer来统一接受来自服务器的所有数据。根据每个包
前面的长度来取包内容。发现包内容的长度不够时,等到下一个数据包到达时再处理。

代码如下:

    // Bytes Buffer
    private var readBuf:ByteArray = new ByteArray();

    // offset记录readBuf的长度
    private var offset:int = 0;

    // 需要等待后续包才能处理的数据的长度
    private var pendingLen:int = 0;

    // Buffer目前读到的位置
    private var read_pos:int = 0;   

    // recv是ProgressEvent.SOCKET_DATA对应的处理函数
    public function recv():void {
    // 首先把服务器发送的数据尽快接受完毕
      while( socket.bytesAvailable > 0 ) {
        var bytesCount:int = socket.bytesAvailable;

        socket.readBytes(readBuf, offset, bytesCount);

        offset += bytesCount;
      }

     // 下面是对Buffer的处理
      var len:int = 0;
      while( offset > read_pos ) {
        // 上个包还有数据没处理完,优先处理
        if( pendingLen > 0 ) {
          withData(readBuf.readUTFBytes(pendingLen));

          read_pos = read_pos + pendingLen + 10;

          pendingLen = 0;

          continue;
        }

        if( readBuf.bytesAvailable == 0 ) {
          break;
        }

        len = Number(readBuf.readUTFBytes(10));

        // 判断Buffer里面的数据是否足够,如果不够,退出结束,等待后续的包解决。
        if( len > readBuf.bytesAvailable ) {
          pendingLen = len;

          break;
        } else {
          withData(readBuf.readUTFBytes(len));

          read_pos = read_pos + len + 10;
        }
      }

     // 表明无等待处理的数据,所有数据都已经被处理完了,可以清空Buffer了,等待后续包处理
      if( pendingLen == 0 ) {
        readBuf.clear();

        offset = 0;

        read_pos = 0;
      }