效果

  • ProtobufVarint32LengthFieldPrepender编码器用于在数据最前面添加Varint32,表示数据长度

  • ProtobufVarint32FrameDecoder是相对应的解码器

Varint32

讲编码器之前,先来讲讲什么是VarInt32(vary int 32),即:可变长的int

在java里,int的长度固定为 4 byte,即 32 bits,最高位为符号位。

而Varint32则不固定长度,最小 1 byte,最大 5 byte,每个byte的最高位如果为1表示下一个byte依然属于Varint32的,为0表示Varint32到当前byte结束。

所以在Varint32中,每个byte只有7bit存储数据。

下面以 398 举例:

  1. // 数字398的二进制表示
  2. 110001110
  3. // 在java中int的二进制表示
  4. 00000000 00000000 00000001 10001110
  5. // Varint32的二进制表示
  6. 10001110 00000011

转换步骤如下:

  1. 398的二进制表示为110001110,总共有9位
  2. 因为398长度为9bit,大于7,所以在Varint32中需要2个byte来存储
  3. Varint32第一个byte存398的低7位0001110,最高位置1表示还未存储完成,即:10001110
  4. Varint32第二个byte存398的后面两位11,最高位置0表示已存储完成,00000011

最后,别问我负数怎么表示,问神奇的海螺。

ProtobufVarint32LengthFieldPrepender

此编码器的作用,就是将数据长度从int转成Varint32,并添加在数据流的最前面。

  1. 查看源码,其入口为decode方法:

    1. @Override
    2. protected void encode(
    3. ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
    4. // 获取数据长度
    5. int bodyLen = msg.readableBytes();
    6. // 计算int转成Varint32所需字节
    7. int headerLen = computeRawVarint32Size(bodyLen);
    8. // 安全扩充缓冲区
    9. out.ensureWritable(headerLen + bodyLen);
    10. // 将bodyLen转成Varint32并写入
    11. writeRawVarint32(out, bodyLen);
    12. // 写入原有的数据msg
    13. out.writeBytes(msg, msg.readerIndex(), bodyLen);
    14. }
  2. 先来看看如何计算int转成Varint32所需字节

    1. static int computeRawVarint32Size(final int value) {
    2. // value的低7位有数据,其余位为0
    3. if ((value & (0xffffffff << 7)) == 0) {
    4. return 1;
    5. }
    6. // value的低14位有数据,其余位为0
    7. if ((value & (0xffffffff << 14)) == 0) {
    8. return 2;
    9. }
    10. if ((value & (0xffffffff << 21)) == 0) {
    11. return 3;
    12. }
    13. if ((value & (0xffffffff << 28)) == 0) {
    14. return 4;
    15. }
    16. return 5;
    17. }
    1. // 0xffffffff 的二进制表示
    2. 11111111 11111111 11111111 11111111
    3. // 0xffffffff << 7 的二进制表示
    4. 11111111 11111111 11111111 10000000
    5. // 假设value为100,二进制表示
    6. 00000000 00000000 00000000 01100100
    7. // value与0xffffffff << 7按位与,即value & (0xffffffff << 7)
    8. 11111111 11111111 11111111 10000000
    9. &
    10. 00000000 00000000 00000000 01100100
    11. =
    12. 00000000 00000000 00000000 00000000 // 结果等于0
    13. // 假设value为398,value与0xffffffff << 7按位与
    14. 11111111 11111111 11111111 10000000
    15. &
    16. 00000000 00000000 00000001 10001110
    17. =
    18. 00000000 00000000 00000001 10000000 // 结果等于384,大于0
  3. 再来看看将bodyLen转成Varint32并写入

    1. static void writeRawVarint32(ByteBuf out, int value) {
    2. while (true) {
    3. // value的低7位有数据,其余位为0
    4. if ((value & ~0x7F) == 0) {
    5. out.writeByte(value);
    6. return;
    7. } else {
    8. // 取value的低7位,最高位置1
    9. out.writeByte((value & 0x7F) | 0x80);
    10. // 右移7位
    11. value >>>= 7;
    12. }
    13. }
    14. }

ProtobufVarint32FrameDecoder

此解码器的作用,是将Varint32 + data,转换成 data

  1. 源码入口

    1. @Override
    2. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
    3. throws Exception {
    4. // 标记读取索引,用户后续的恢复
    5. in.markReaderIndex();
    6. // 读取前的索引位置
    7. int preIndex = in.readerIndex();
    8. // 读取字节,将Varint32转成int
    9. int length = readRawVarint32(in);
    10. // 读取后索引位置等于读取前,表示读取不成功
    11. if (preIndex == in.readerIndex()) {
    12. return;
    13. }
    14. if (length < 0) {
    15. throw new CorruptedFrameException("negative length: " + length);
    16. }
    17. // 如果netty读取到的字节长度不满足数据长度,则重置读取索引
    18. if (in.readableBytes() < length) {
    19. in.resetReaderIndex();
    20. } else {
    21. out.add(in.readRetainedSlice(length));
    22. }
    23. }
  2. 读取字节,将Varint32转成int

    1. private static int readRawVarint32(ByteBuf buffer) {
    2. if (!buffer.isReadable()) {
    3. return 0;
    4. }
    5. buffer.markReaderIndex();
    6. byte tmp = buffer.readByte();
    7. // tmp >= 0,则byte最高位为0,证明Varint32长度为1byte
    8. if (tmp >= 0) {
    9. return tmp;
    10. } else {
    11. // result取temp的低7位
    12. int result = tmp & 127;
    13. // Varint还没结束,但netty读不到更多字节了,则返回
    14. if (!buffer.isReadable()) {
    15. buffer.resetReaderIndex();
    16. return 0;
    17. }
    18. // 读取下一个字节,并判断Varint是否在该字节结束
    19. if ((tmp = buffer.readByte()) >= 0) {
    20. result |= tmp << 7;
    21. } else {
    22. // 如果Varint在第二个字节还没结束,则取第二个字节的低7位
    23. result |= (tmp & 127) << 7;
    24. if (!buffer.isReadable()) {
    25. buffer.resetReaderIndex();
    26. return 0;
    27. }
    28. if ((tmp = buffer.readByte()) >= 0) {
    29. result |= tmp << 14;
    30. } else {
    31. result |= (tmp & 127) << 14;
    32. if (!buffer.isReadable()) {
    33. buffer.resetReaderIndex();
    34. return 0;
    35. }
    36. if ((tmp = buffer.readByte()) >= 0) {
    37. result |= tmp << 21;
    38. } else {
    39. result |= (tmp & 127) << 21;
    40. if (!buffer.isReadable()) {
    41. buffer.resetReaderIndex();
    42. return 0;
    43. }
    44. result |= (tmp = buffer.readByte()) << 28;
    45. if (tmp < 0) {
    46. throw new CorruptedFrameException("malformed varint.");
    47. }
    48. }
    49. }
    50. }
    51. return result;
    52. }
    53. }

Netty-ProtobufVarint32的更多相关文章

  1. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  2. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  3. 从netty-example分析Netty组件续

    上文我们从netty-example的Discard服务器端示例分析了netty的组件,今天我们从另一个简单的示例Echo客户端分析一下上个示例中没有出现的netty组件. 1. 服务端的连接处理,读 ...

  4. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  5. 从netty-example分析Netty组件

    分析netty从源码开始 准备工作: 1.下载源代码:https://github.com/netty/netty.git 我下载的版本为4.1 2. eclipse导入maven工程. netty提 ...

  6. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  7. Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇

    目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...

  8. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  9. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

  10. JAVA通信系列三:Netty入门总结

    一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...

随机推荐

  1. Go 语言快速开发入门

    目录 需求 开发的步骤 linux下如何开发Go程序 MAC下如何开发Go程序 Golang执行流程分析 编译和运行说明 Go程序开发的注意事项 Go语言的转义字符(escapechar) Golan ...

  2. 看 AWS 如何通过 Nitro System 构建竞争优势

    目录 目录 目录 前言 Amazon Nitro System Overview AWS EC2 的虚拟化技术演进之路 Nitro Hypervisor Nitro Cards Nitro Contr ...

  3. 在字节跳动,一个更好的企业级SparkSQL Server这么做

    SparkSQL是Spark生态系统中非常重要的组件.面向企业级服务时,SparkSQL存在易用性较差的问题,导致难满足日常的业务开发需求.本文将详细解读,如何通过构建SparkSQL服务器实现使用效 ...

  4. 并发编程系列之Lock锁可重入性与公平性

    一.相似之处:Lock锁 vs Synchronized 代码块 Lock锁是一种类似于synchronized 同步代码块的线程同步机制.从Java 5开始java.util.concurrent. ...

  5. VMware虚拟机中安装Linux操作系统(ubuntu)

    一.准备工作: 1.下载VMware虚拟机 下载地址:https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evalua ...

  6. 【Java面试】Zookeeper中的Watch机制的原理?

    一个工作了7年的粉丝,遇到了一个Zookeeper的问题. 因为接触过Zookeeper这个技术,不知道该怎么回答. 我说一个工作了7年的程序员,没有接触过主流技术,这不正常. 于是我问了他工资以后, ...

  7. c 语言彩票选号

    最近刚学了c语言,就做了个彩票选号程序练手玩玩,做的不好请见谅 1.分为前区(1-35)和后区(1-12)号码 2.先循环随机前区号在循环后区号 3.生成随机时数判断是否有重复值,和之前5期是否出现过 ...

  8. [漏洞复现] [Vulhub靶机] Tomcat7+ 弱口令 && 后台getshell漏洞

    免责声明:本文仅供学习研究,严禁从事非法活动,任何后果由使用者本人负责. 0x00 背景知识 war文件 0x01 漏洞介绍 影响范围:Tomcat 8.0版本 漏洞类型:弱口令 漏洞成因:在tomc ...

  9. Unity-自定义事件派发器的两次尝试

    一.前言: 在游戏开发的很多时候,需要引用其他类的方法,但是一旦类多起来了,相互引用会导致引用关系混乱,极其难以阅读. 以前初次做抖音小游戏时,和一位经验老道的cocos程序员合作,看到我写的代码他不 ...

  10. 2020 CSP-J 初赛游记

    估分 预估 85 分,一是怕选错,而是最后真的错了一些 考点 排列组合:论临时抱佛脚的作用 靠前看了一下捆绑法和插板法,果然考了. 2.算法常识,和复杂度分析 冒泡排序最小交换次数 = n-1 , G ...