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

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

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

固定长度数值的网络发送

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

//java
int moeny = 100  //用户的金钱,int 4个bytes
int playerId = 100000001;
//AS3
var moeny:int  = 100;
var playerId:int =  100000001;

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

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

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

//AS3
var bytes:ByteArray = new ByteArray();
bytes.writeInt(playerId);
bytes.writeInt(moeny);
//java 接受数据
ByteBuf buf = frame.content();
int money = buf.readInt();
int playerId = buf.readInt();

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

//AS3
var type:int = 5;
var bytes:ByteArray = new ByteArray();
bytes.writeByte(type);
//java 接受数据
ByteBuf buf = frame.content();
int type = buf.readByte();

这样是属于主动地节省字节,因为已经明确知道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);
}

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();
}

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

数组长度的传输方式

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

/**
 * 动态写数组的长度
 * @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);
}

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

附录: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);
}
发布了92 篇原创文章 · 获赞 72 · 访问量 78万+

猜你喜欢

转载自blog.csdn.net/sujun10/article/details/61933029