网游通讯传输可变长度的数值和数组

来源:互联网 发布:java递归1加到100 编辑:程序博客网 时间:2024/06/05 00:44

何为可变长度的数值(int,long等)

其实现在大部分网络游戏(端游、页游、手游都一样),在客户端和服务端通讯,关于数值都会采用可变长度的方式来传输,从简而减小通讯量。 
一般情况,客户端和服务端进行网络的socket通讯,都是采用二进制数值来进行的(也有采用字符串)。 
可变长度的int是指根据实际的数值在网络传输中动态地改变长度。比如int在传输中可以变为byte,short,从而减少int的长度。文本会就Java服务端,H5和AS3客户端之间的通讯来进行讲解。

固定长度数值的网络发送

表示数组的长度一般都是固定,比如short是2个字节,int4个字节,long8个字节。那么客户端和服务端通讯的时候,传输short,int等数据过程的时候,如果不实现一些算法,则是直接传输,那么就是数值类型有多少个字节,就往数据流(ByteArray)写多少个字节。 
但是实际应用中,虽然定义了一个数值类型,但是他表示的值经常在byte,short,int等不同范围之间切换。比如定义用户的金钱

//javaint moeny = 100  //用户的金钱,int 4个bytesint playerId = 100000001;//AS3var moeny:int  = 100;var playerId:int =  100000001;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

js只有number,如果不进行字节长度控制的话,每次都得发8字节过去给js客户端

//js 只有number,8个字节var money = 100;var playerId = 100000001;
  • 1
  • 2
  • 3

很显然,如果只发送一个10000或者127以内的数值,直接写4个字节的int,是非常浪费流量。 
比如发送moeny这个字段就浪费了,playerId比较大那就是正常采用int了。js的话损失更大了,number是8个字节来进行发送。 
所以一些好的通讯协议库会采用根据数值的实际值大小来动态发送byte,short,int这些类型。 
然后再收到的那一端再把byte,short转换成int或者long,number类型。 
比如Google protobuf就可以把通讯的内容压缩得非常小。 
当然具体网络传输协议采用什么样的方式,是个比较广的问题,一般是要根据项目的实际情况来处理,不在本章的讨论范围。 
固定长度的发送和接受代码,比如发送playerId和moeny到服务端(演示为伪代码,后面会给出全部代码)

//AS3var bytes:ByteArray = new ByteArray();bytes.writeInt(playerId);bytes.writeInt(moeny);//java 接受数据ByteBuf buf = frame.content();int money = buf.readInt();int playerId = buf.readInt();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

客户端和服务端收发保持一直,这样就可以进行通讯了。 
实际项目中,也会对一些数值定义类型做优化,不会全部统一int或者number。 
比如表示类型,虽然显示的时候是用int来表示,但是实际写给服务器的时候,是采用采用byte。

//AS3var type:int = 5;var bytes:ByteArray = new ByteArray();bytes.writeByte(type);//java 接受数据ByteBuf buf = frame.content();int type = buf.readByte();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这样是属于主动地节省字节,因为已经明确知道type不会超过127,所以写byte类型是安全的。

发送可变长度的数值的通讯机制

有些数据的值变化比较大,可能是0到上百万之间的变化,这种情况,我们只能使用int来表。比如money(游戏中的元宝)。刚开始玩家可能只有几十个元宝或者0,久一点可能1,2万了。土豪可能直接来个10w8w的。 
假如我们还是发送定长的int,对于没有元宝或者低于30000元宝的玩家来说,就浪费通讯字节了。 
所以原理是这样的,每次发送的时候,判断一下数值的大小,默认是写byte,如果大于或者等于某个规定值,则是表示类型。比如发送一个int到服务器去,下面的演示代码: 
//先判断数据的长度

    /**    * 写可变的整形数据类型     * @param bytes 二进制数组    * @param value    */          public static function writeVaryInt(bytes:ByteArray,value:int):void    {        //用-128、-127,-126来做标识符,分别表示short int 和long类型        if(value <= 127 && value > -126)        {            //最小范围内,直接写byte            bytes.writeByte(value);            return ;        }        if(value <= 32767 && -value >= -32768)        {            //short -128            bytes.writeByte(-128);            bytes.writeShort(value);            return ;        }        if(value <= 2147483647 && -value >= -2147483648)        {            //int -127            bytes.writeByte(-127);            bytes.writeInt(value);            return ;        }        //剩下的都是long了        bytes.writeByte(-126);        bytes.writeDouble(value);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

Java对应的读取内容是这样的:

/** * 读取可变长度的整形数据  * @param bytes 二进制数据 * @return 相应的整形 */     public static long readVaryInt( ByteBuf bytes){    byte type = bytes.readByte();    //byte    if(type <= 127 && type > -126)        return type;    //short    if(type == -128)        return bytes.readShort();    //int    if(type == -127)        return bytes.readInt();    //double(long)    return (long)bytes.readDouble();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

其实还可以进一步去扩展,比如还可以写单精度和双精度,以及混合之类等等。

数组长度的传输方式

可以很容易地根据这个原理,来写数组长度,而且数组长度比数值更简单。

/** * 动态写数组的长度 * @param bytes * @param ary */public static function writeArySize(bytes:ByteArray,ary:Array):void{    //数组的int长度比较清晰,不会有负的,所以采用-1来表示    var len:int = ary.length;    if(len <= 127 )    {        bytes.writeByte(len);        return ;    }    if(len <= 32767)    {        //写个标识符        bytes.writeByte(-1);        //写个short的长度        bytes.writeShort(length);        return ;    }    //剩下都写int了,long不用想,太可怕    bytes.writeByte(-2);    //写个short的长度    bytes.writeInt(length);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

读取就不贴了,只要按照写的规则读出来就行了。

附录:Html5版本的可变长度数值读取方法

这里采用的是白鹭引擎写的,语言是TypeScript。跟AS3的几乎一样的逻辑和写法。

/** * 写可变的整形数据类型 * @param bytes 二进制数组 * @param value */static writeVaryInt(bytes:egret.ByteArray,value:number):void{    //用-128、-127,-126来做标识符,分别表示short int 和long    //写byte    if(value <= 127 && value > -126)    {        bytes.writeByte(value);        return ;    }    if(value <= 32767 && -value >= -32768)    {        //short -128        bytes.writeByte(-128);        bytes.writeShort(value);        return ;    }    if(value <= 2147483647 && -value >= -2147483648)    {        //int -127        bytes.writeByte(-127);        bytes.writeInt(value);        return ;    }    //剩下的都是long了    bytes.writeByte(-126);    bytes.writeDouble(value);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
版权声明:本文为博主原创文章,转载必须声明出处和作者。地址:http://blog.csdn.net/sujun10 作者:弃天笑
原创粉丝点击