Socket+AMF3粘包问题


  问题:
  服务端Mina 前台接收Flex   
  参照的原型为http://www.klstudio.com/post/202.html
  
  经过个人调试  分别在服务端和客户端收发消息时进行长度处理
  但是在服务端大量发送数据 时,还是会发生客户端粘包的状况,导致发生错误,而且也比较频繁。
  求服务端与客户端的收发代码 段。要实用的。
  (最好是经过一定压力测试的。或者发上来我压一下。)
  补充说明一次我打断点跟踪到的情况:
  客户端接收到信息:
  获取了长度:长度为34字节   查看socket里面也确实还有34字节的内容
  但是这时候就读取object就会出现异常
  flex.messaging.io.UnknownTypeException: Unknown AMF type '120'
  120这个就是34字节当中的第一个的内容  还有其他的比如 -124 之类的等等。
  解答:
  这个基本是socket编程都会遇到的经典问题
  amf本身是一种2进制封装,连发时接收方可能会因为数据堆积在一起而分不清包的头尾,造成映射错误。
  解决办法就是在发包前对amf进行简单封包,因为amf本身就是2进制格式,那么只需要在其之前写入amf长度(即正文长度),接收方,先读取包头的长度,再固定取出其后的正文。取完正文,再取包头,依次取完。
  每个发出的包,结构为 理论说完,就给出关键代码,相信认真研究过socket的一看就知:
  as3  --->  Server    发送部分 public function send(obj:Object)
  {    
  var objByte:ByteArray = new ByteArray();
  objByte.writeObject(obj); 
  // objByte.compress();   //压缩,可以省略
  var msgByte:ByteArray = new ByteArray();
  msgByte.writeInt(objByte.length); 
  msgByte.writeBytes(objByte, 0, objByte.length); 
  sk.writeBytes(msgByte);
  sk.flush();   
  }
  server --> as3  接收部分 //一接受到数据就尝试读
  private function onSkData(e:ProgressEvent):void
  {
  this.readData();
  }
  private function readData():void
  {     
  //如果还没读过头部则读一次。
  if (!this.isReadHead && this.sk.bytesAvailable > 4)
  {
  var lenByte:ByteArray = new ByteArray();
  sk.readBytes(lenByte, 0, 4);
  this.msgLen = lenByte.readInt();
  //trace("新的消息长度:" + this.msgLen);
  this.isReadHead = true;
  }
  //如果读了头部,并且当前可读长度大于等于消息长度,则开始读取
  if (isReadHead && this.sk.bytesAvailable >= this.msgLen)
  {
  var objByte:ByteArray = new ByteArray();
  sk.readBytes(objByte, 0, this.msgLen); 
  this.isReadHead = false;
  //objByte.uncompress();
  var obj:Object = objByte.readObject(); 
  this.readMsg(obj);   //读完了,可以去解释了
  } 
  //如果是读过头,则如果当前消息大于消息长度则再次调用读取,否则则判断是否可读头部
  this.sk.bytesAvailable > 0 && this.readData();
  }  
  至于server端,其原理是一样的,都是读的时候先读头,发的时候也是先发个头。
  晚上回去再补充一下
  -----------
  补充server端代码(C#,解码类为FluorineFx,仅供参考思路)
  server -> as3 发送 public void send(object obj)
  {
  try
  {
  ByteArray objByte = new ByteArray();
  objByte.WriteObject(obj);
  // objByte.Compress(); 
  byte[] objBuff = new byte[objByte.Length];
  objByte.Position = 0;
  objByte.ReadBytes(objBuff, (uint)0, (uint)objBuff.Length);
  ByteArray msgByte = new ByteArray();
  msgByte.WriteInt(objBuff.Length);
  msgByte.WriteBytes(objBuff, 0, objBuff.Length);
  byte[] msgBuff = new byte[msgByte.Length];
  msgByte.Position = 0;
  msgByte.ReadBytes(msgBuff, (uint)0, (uint)msgBuff.Length);
  //发送
  this.ng.sk.BeginSend(msgBuff, 0, msgBuff.Length, SocketFlags.None, onSendOver, this.ng);
  }
  catch
  {
  Core.mainForm.addLog("发送失败");
  }
  }
  as3 -> server 接受     //接收完的回调
  private void receOver(IAsyncResult ar)
  {
  NetGate ng = (NetGate)ar.AsyncState;
  int size = ng.sk.EndReceive(ar);
  // Core.mainForm.addLog("接受到总长度:" + size);
  int ind = 0;
  if (size > 0)
  { 
  while (size > 0)
  {
  //读消息头
  MemoryStream lenMs = new MemoryStream(ng.buff, ind, 4);
  ByteArray lenByte = new ByteArray(lenMs);
  Int32 len = lenByte.ReadInt();
  // Core.mainForm.addLog("消息长度:" + len.ToString());
  if (len == 0) { break; }
  //根据头长度取内容
  MemoryStream ms = new MemoryStream(ng.buff, ind + 4, len);
  ByteArray ba = new ByteArray(ms);
  // ba.Uncompress(); 
  object obj = ba.ReadObject();
  this.readMsg(obj);
  ind += (len + 4);
  size -= (len + 4);
  }
  this.ng.buff.Initialize();
  ng.sk.BeginReceive(this.ng.buff, 0, this.ng.buffSize, SocketFlags.None, this.onReceOver, this.ng);
  }
  else
  { 
  ng.sk.Close();
  this.offLine();
  }
  }
  发表于 @ 2010年10月10日 00:45:00 |

猜你喜欢

转载自gzxpz58y.iteye.com/blog/1572907