设为首页 加入收藏

TOP

Netty Protobuf处理粘包分析(二)
2023-07-26 08:16:39 】 浏览:94
Tags:Netty Protobuf 包分析
0 1

忽略符号右移其实就是将后7位挤出去,在前边补7个0。

计算机中一般首位是符号位,0表示正数,1表示负数。

这里需要注意是>>表示右移,不改变符号,最高位与原来符号保持一致。 >>>是忽略符号位右移,最高位补0。

200 >>>= 7的结果为00000001,继续走if判断,满足条件,将00000001写入bytebuf

最终value=200写入bytebuf的字节是1100100000000001

至此,三个看不懂的位运算都理解了,那么我们连起来看一下:

如果value可以用7个字节表示(或者说是value在0~127范围内),将value转换为字节写入bytebuf,跳出循环,方法结束。

如果value不能用7个字节表示(或者说是value不在0~127范围内),取最后7个位,高位补1,写入bytebuf中,右移7位(将刚才取出的7位删掉),再次判断是否满足if条件,不满足就继续上面的操作,直到满足条件为止。

总结一下writeRawVarint32方法,其实是把一个整数拆分成多个字节,倒序写入bytebuf中,如果将每个字节转换为byte类型,最后一个字节总是正数,前面的字节都是负数。我们可以猜测,接收消息时以第一个正数为分割,将表示消息体长度的字节与消息体字节拆分开,再通过位运算将前者组合起来就得到了消息体的长度。

computeRawVarint32Size()方法

我们在writeRawVarint32方法分析中了解了位运算,再看computeRawVarint32Size方法就很简单了。

计算机表示负数

0xffffffff转换为二进制是11111111 1111111 11111111 11111111转换为有有符号int类型是-1。为什么是-1?

因为计算机使用二进制可以做加法运算,但是没办法做减法运算,加上一个负数就相当于做了减法运算,现在问题是如何表示负数?

曾经有原码表示法、反码表示法,这里不做赘述,现在使用的是补码表示法。

补码表示法是将正数的二进制取反,然后在最后一位+1。

通过例子看一下:

有符号int类型的1用二进制可以表示为00000000 0000000 00000000 0000001取反得到11111111 11111111 11111111 11111110+1得到11111111 11111111 11111111 11111111转换为十六进制是0xffffffff

计算value & (0xffffffff << 7)

<<表示左移,从左边挤出去7个位,在右边补7个0。

这里仍然假设value分为为100,200。

// 计算(100 & (0xffffffff <<  7))
  00000000 0000000 00000000 01100100 // 100
& 11111111 1111111 11111111 10000000 // 0xffffffff <<  7 
  00000000 0000000 00000000 00000000 // 结果:0

// 计算(200 & (0xffffffff <<  7))
  00000000 0000000 00000000 11001000 // 200
& 11111111 1111111 11111111 10000000 // 0xffffffff <<  7
  00000000 0000000 00000000 10000000 // 结果:128

// 计算(200 & (0xffffffff <<  14))
  00000000 0000000 00000000 11001000 // 200
& 11111111 1111111 11000000 00000000 // 0xffffffff <<  14
  00000000 0000000 00000000 00000000 // 结果:0

从以上计算可以看出,如果value可以用小于7个位来表示,则左移7个位可以满足,如果value可以用8~14个位来表示,左移14个位可以满足。

100、200计算结果分别为1、2,与writeRawVarint32方法写入的字节数量一致。

writeRawVarint32是方法7个7个的取出位,这里按7个位来计算所需字节数量,最终返回表示消息体长度的字节数量。

解码类

public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {

    // TODO maxFrameLength + safe skip + fail-fast option
    //      (just like LengthFieldBasedFrameDecoder)

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        in.markReaderIndex();
        int preIndex = in.readerIndex();
        int length = readRawVarint32(in);
        if (preIndex == in.readerIndex()) {
            return;
        }
        if (length < 0) {
            throw new CorruptedFrameException("negative length: " + length);
        }

        if (in.readableBytes() < length) {
            in.resetReaderIndex();
        } else {
            out.add(in.readRetainedSlice(length));
        }
    }

    /**
     * Reads variable length 32bit int from buffer
     *
     * @return decoded int if buffers readerIndex has been forwarded else nonsense value
     */
    private static int readRawVarint32(ByteBuf buffer) {
        if (!buffer.isReadable()) {
            return 0;
        }
        buffer.markReaderIndex();
        byte tmp = buffer.readByte();
        if (tmp >= 0) {
            return tmp;
        } else {
            int result = tmp & 127;
            if (!buffer.isReadable()) {
                buffer.resetReaderIndex();
                return 0;
            }
            if ((tmp = buffer.readByte()) >= 0) {
                result |= tmp << 7;
            } else {
                result |= (tmp & 127) << 7;
                if (!buffer.isReadable()) {
                    buffer.resetReaderIndex();
                    return 0;
                }
                if ((tmp = buffer.readByte()) >= 0) {
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Ubuntu玩机记录,让我破电脑又飞.. 下一篇day05-SpringMVC底层机制简单实现..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目