Netty Protobuf处理粘包分析(二)
忽略符号右移其实就是将后7位挤出去,在前边补7个0。
计算机中一般首位是符号位,0表示正数,1表示负数。
这里需要注意是>> 表示右移,不改变符号,最高位与原来符号保持一致。 >>> 是忽略符号位右移,最高位补0。
200 >>>= 7 的结果为00000001 ,继续走if 判断,满足条件,将00000001 写入bytebuf 。
最终value=200 写入bytebuf 的字节是11001000 、00000001 。
至此,三个看不懂的位运算都理解了,那么我们连起来看一下:
如果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) {
|