SOCKET 封包 和 解包

目标

soecet收发数据封包和解包

背景

一般都有固定长度、固定后缀等做法,但都花里胡哨,有各自的问题。
本文采用数据长度+ 数据buffer模式,其中最简单的是用4字节int来表示长度。但这还不够,很多情况下长度可能一个byte就够用了,浪费了3个字节,本文采用动态字节数来存储长度,至于数据buffer使用protobuf来编码和解码就完事了

详细方案

  1. 首先读取一个字节,也就8个bit,a b c d e f g h
  2. 判断a是否为0,如果是,则长度用1个字节0xxx xxxx表示,那么1个字节能表示的范围是[0, 128{0x80} ),
  3. a=1,继续判断b是否为0,如果是,长度用2个字节10xx xxxx xxxx xxxx表示,2个字节表示的范围是[128, 16k{0x4000} )
  4. a=1,b=1,继续判断c是否为0,如果是,长度用3个字节表示110x xxxx xxxx xxxx xxxx,3个字节表示的范围是[16k, 2M{0x200000} )
  5. a=1,b=1,c=1,此时长度用4个字节表示111x xxxx xxxx xxxx xxxx xxxx xxxx xxxx,4个字节表示的范围是[2M, 512M{0x20000000} )
首字节类型需要字节数范围16进制最大值(不包含)
0xxx xxxx1[0, 128)0x80
10xx xxxx2[128, 16k)0x4000
110x xxxx3[16k, 2M)0x200000
111x xxxx4[2M, 512M)0x20000000

其实绝大部分包的大小是128字节以内,那么将会用1个字节来表示长度,相对原始方案,节省了3个字节

代码

  1. 长度编码

        /// <summary>
        /// 设置一个byte中各个bit的位值
        /// </summary>
        /// <param name="_word">目标,要设置的byte</param>
        /// <param name="value">值,为目标位设置的值</param>
        /// <returns></returns>
        public static byte SetBits(byte _word, byte value)
        {
            return (byte)(_word & ~value | value);
        }

        /// <summary>
        /// 0xxx xxxx [0-128{0x80})  1个字节
        /// 10xx xxxx [128, 16k{0x4000}) 2个字节
        /// 110x xxxx [16k, 2M{0x200000}) 3个字节
        /// 111x xxxx [2M, 512M{0x20000000}) 4个字节
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] EncodeInt(int value)
        {
            if (value < 0)
            {
                throw new ArgumentException(string.Format("invalid data length < 0, cur_length={0}", value));
            }
            else if (value < 0x80)
            {
                return new byte[] { (byte)value };
            }
            else if (value < 0x4000)
            {
                return new byte[] { SetBits((byte)(value >> 8), 0x80), (byte)value };
            }
            else if (value < 0x200000)
            {
                return new byte[] { SetBits((byte)(value >> 16), 0xC0), (byte)(value >> 8), (byte)value };
            }
            else if (value < 0x20000000)
            {
                return new byte[] { SetBits((byte)(value >> 24), 0xE0), (byte)(value >> 16), (byte)(value >> 8), (byte)(value) };
            }
            else
            {
                throw new ArgumentException(string.Format("invalid data length, max_length({0}), cur_length({1})", 0x20000000, value));
            }
        }
  1. 长度解码

        public static int DecodeInt(byte[] data, int offset, int count, out int lengthByteCount)
        {
            if ((data[offset] & 0x80) == 0)//0xxx xxxx
            {
                lengthByteCount = 1;
                if (count < lengthByteCount) return -1;
                return (int)data[offset];
            }
            else if ((data[offset] & 0x40) == 0)//10xx xxxx
            {
                lengthByteCount = 2;
                if (count < lengthByteCount) return -1;

                return (data[offset] & 0x3F) << 8 | data[offset + 1];
            }
            else if ((data[offset] & 0x20) == 0)//110xx xxxx
            {
                lengthByteCount = 3;
                if (count < lengthByteCount) return -1;
                return (data[offset] & 0x1F) << 16 | data[offset + 1] << 8 | data[offset + 2];
            }
            else//111x xxxx
            {
                lengthByteCount = 4;
                if (count < lengthByteCount) return -1;
                return (data[offset] & 0x1F) << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
            }
        }
    }
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值