问题:
服务端Mina 前台接收Flex
参照的原型为http://www.klstudio.com/post/202.html
<[Socket +AMF3]给合Flash的Socket和AMF3来尝试开发 web游戏 >
经过个人调试 分别在服务端和客户端收发消息时进行长度处理
但是在服务端大量发送数据 时,还是会发生客户端粘包的状况,导致发生错误,而且也比较频繁。
求服务端与客户端的收发代码 段。要实用的。
(最好是经过一定压力测试的。或者发上来我压一下。)
补充说明一次我打断点跟踪到的情况:
客户端接收到信息:
获取了长度:长度为34字节 查看socket里面也确实还有34字节的内容
但是这时候就读取object就会出现异常
flex .messaging.io.UnknownTypeException: Unknown AMF type '120'
120这个就是34字节当中的第一个的内容 还有其他的比如 -124 之类的等等。
解答:
这个基本是socket编程都会遇到的经典问题
amf本身是一种2进制封装,连发时接收方可能会因为数据堆积在一起而分不清包的头尾,造成映射错误。
解决办法就是在发包前对amf进行简单封包,因为amf本身就是2进制格式,那么只需要在其之前写入amf长度(即正文长度),接收方,先读取包头的长度,再固定取出其后的正文。取完正文,再取包头,依次取完。
每个发出的包,结构为
struct
{
DWORD Data_LEN //包长
CHAR[..] //内容
}
理论说完,就给出关键代码,相信认真研究过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(); } }