netty源码阅读之ByteBuf之ByteBuf结构和重要API

怼烎@ 2022-05-14 22:14 477阅读 0赞

主要分一下几点讲解:

1、ByteBuf结构

2、read、write、set方法

3、mark和reset方法

一、ByteBuf结构

我们找到ByteBuf源码,查看最前面的注释,有这样一段:

  1. * <pre>
  2. * +-------------------+------------------+------------------+
  3. * | discardable bytes | readable bytes | writable bytes |
  4. * | | (CONTENT) | |
  5. * +-------------------+------------------+------------------+
  6. * | | | |
  7. * 0 <= readerIndex <= writerIndex <= capacity
  8. * </pre>

在netty里面,有几个重要的指针:

1、readerIndex,0到readerindex这里就是不读的数据,也就是抛弃的数据;从readerIndex开始读数据。

2、writerIndex,readerIndex到writerIndex这里就是没有读的数据,也就是可读的内容;从writeIndex开始写数据。

3、capacity,writerIndex到capacity这段数据就是我们可以往里面写的空间

4、maxCapacity,其实这里应该还有个maxCapacity(可以看做是在capacity后面),capacity到maxCapacity这里,是这个Byte还可以扩展的空间。

二、read、write、set方法

我们找到ByteBuf的read方法,发现它居然有这么多种实现:

  1. ...
  2. public abstract boolean readBoolean();
  3. public abstract byte readByte();
  4. public abstract short readUnsignedByte();
  5. public abstract short readShort();
  6. public abstract short readShortLE();
  7. public abstract int readUnsignedShort();
  8. public abstract int readUnsignedShortLE();
  9. public abstract int readMedium();
  10. ....

当然这里之列举了一部分,netty会根据数据的大小选择合适的read方法。

write也不用说了:

  1. ...
  2. public abstract ByteBuf writeBoolean(boolean value);
  3. public abstract ByteBuf writeByte(int value);
  4. public abstract ByteBuf writeShort(int value);
  5. public abstract ByteBuf writeShortLE(int value);
  6. ...

set方法:

  1. ...
  2. public abstract ByteBuf setBoolean(int index, boolean value);
  3. public abstract ByteBuf setByte(int index, int value);
  4. public abstract ByteBuf setShort(int index, int value);
  5. public abstract ByteBuf setShortLE(int index, int value);
  6. ...

read和write是做什么的不用多解释,这里我们介绍以下set,其实也就是在特定的index开始的位置设置上特定的值,可以从index位置开始,设置多个。

三、mark和reset方法

这里先介绍以下,mark我标记在某个位置,执行read或者write之后指针会变化,我用reset就可以设置回去原来mark的位置了:

  1. /**
  2. * Marks the current {@code readerIndex} in this buffer. You can
  3. * reposition the current {@code readerIndex} to the marked
  4. * {@code readerIndex} by calling {@link #resetReaderIndex()}.
  5. * The initial value of the marked {@code readerIndex} is {@code 0}.
  6. */
  7. public abstract ByteBuf markReaderIndex();
  8. /**
  9. * Repositions the current {@code readerIndex} to the marked
  10. * {@code readerIndex} in this buffer.
  11. *
  12. * @throws IndexOutOfBoundsException
  13. * if the current {@code writerIndex} is less than the marked
  14. * {@code readerIndex}
  15. */
  16. public abstract ByteBuf resetReaderIndex();
  17. /**
  18. * Marks the current {@code writerIndex} in this buffer. You can
  19. * reposition the current {@code writerIndex} to the marked
  20. * {@code writerIndex} by calling {@link #resetWriterIndex()}.
  21. * The initial value of the marked {@code writerIndex} is {@code 0}.
  22. */
  23. public abstract ByteBuf markWriterIndex();
  24. /**
  25. * Repositions the current {@code writerIndex} to the marked
  26. * {@code writerIndex} in this buffer.
  27. *
  28. * @throws IndexOutOfBoundsException
  29. * if the current {@code readerIndex} is greater than the marked
  30. * {@code writerIndex}
  31. */
  32. public abstract ByteBuf resetWriterIndex();

看看源码的注释就能明白

四、其他API

在一个开始我们也介绍过这几个指针,看看注释,就知道这些方法的作用:

  1. /**
  2. * Returns the number of readable bytes which is equal to
  3. * {@code (this.writerIndex - this.readerIndex)}.
  4. */
  5. public abstract int readableBytes();
  6. /**
  7. * Returns the number of writable bytes which is equal to
  8. * {@code (this.capacity - this.writerIndex)}.
  9. */
  10. public abstract int writableBytes();
  11. /**
  12. * Returns the maximum possible number of writable bytes, which is equal to
  13. * {@code (this.maxCapacity - this.writerIndex)}.
  14. */
  15. public abstract int maxWritableBytes();
  16. /**
  17. * Returns {@code true}
  18. * if and only if {@code (this.writerIndex - this.readerIndex)} is greater
  19. * than {@code 0}.
  20. */
  21. public abstract boolean isReadable();
  22. /**
  23. * Returns {@code true} if and only if this buffer contains equal to or more than the specified number of elements.
  24. */
  25. public abstract boolean isReadable(int size);
  26. /**
  27. * Returns {@code true}
  28. * if and only if {@code (this.capacity - this.writerIndex)} is greater
  29. * than {@code 0}.
  30. */
  31. public abstract boolean isWritable();
  32. /**
  33. * Returns {@code true} if and only if this buffer has enough room to allow writing the specified number of
  34. * elements.
  35. */
  36. public abstract boolean isWritable(int size);
  37. /**
  38. * Sets the {@code readerIndex} and {@code writerIndex} of this buffer to
  39. * {@code 0}.
  40. * This method is identical to {@link #setIndex(int, int) setIndex(0, 0)}.
  41. * <p>
  42. * Please note that the behavior of this method is different
  43. * from that of NIO buffer, which sets the {@code limit} to
  44. * the {@code capacity} of the buffer.
  45. */
  46. public abstract ByteBuf clear();

我们可以知道,ByteBuf的读写什么,其实就是不停地移动这些指针。初始的位置readerIndex和writerIndex的位置都是0。

五、AbstactByteBuf

首先我们查看一下这个类的关系图:

70

我们暂且不管下面的几个实现吧(下一篇再介绍),直接先看AbstractByteBuf,继承于ByteBuf,看这个类的源码的解释:

  1. /**
  2. * A skeletal implementation of a buffer.
  3. */
  4. public abstract class AbstractByteBuf extends ByteBuf {
  5. ...
  6. }

a skeletal implementation。对buffer也就是ByteBuf的一个骨架式的实现。阅读源码多了,我们都知道这个套路,abstract一般就是对接口的骨架式实现。

继续看:

  1. int readerIndex;
  2. int writerIndex;
  3. private int markedReaderIndex;
  4. private int markedWriterIndex;
  5. private int maxCapacity;

前面介绍的那几个指针,都在这里体现出来了。

前面说过的maxCapacity:

  1. protected AbstractByteBuf(int maxCapacity) {
  2. if (maxCapacity < 0) {
  3. throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)");
  4. }
  5. this.maxCapacity = maxCapacity;
  6. }

readableBytes()的实现:

  1. @Override
  2. public int readableBytes() {
  3. return writerIndex - readerIndex;
  4. }

writableBytes(),maxWritableBytes(),markReaderIndex(),resetReaderIndex()这几个不赘述,自行查看源码,简单。

然后我们看ByteBuf readerIndex(int readerIndex):

  1. @Override
  2. public ByteBuf readerIndex(int readerIndex) {
  3. if (readerIndex < 0 || readerIndex > writerIndex) {
  4. throw new IndexOutOfBoundsException(String.format(
  5. "readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
  6. }
  7. this.readerIndex = readerIndex;
  8. return this;
  9. }

保证不越界,并且附上读指针。从这里我们开始留意到,ByteBuf很多都是返回this指针,方便链式调用。

看readByte():

  1. @Override
  2. public byte readByte() {
  3. checkReadableBytes0(1);
  4. int i = readerIndex;
  5. byte b = _getByte(i);
  6. readerIndex = i + 1;
  7. return b;
  8. }

我们看到,读的操作是委托到有一个下划线的_getByte(i)方法里面,别的readBool(),readInt()等这些方法,都是一个道理。

在这里,由于byte是一个字节,所以readerIndex是i+1;

如果是readInt():

  1. @Override
  2. public int readInt() {
  3. checkReadableBytes0(4);
  4. int v = _getInt(readerIndex);
  5. readerIndex += 4;
  6. return v;
  7. }

int 是个字节,那么就是加4。

至于下划线方法的实现,会委托给每种ByteBuf最终子类的实现,有各自的方法,这就关系到各个ByteBuf实现的区别了,后续的文章会继续讲解。(readableBytes(),writableBytes(),maxWritableBytes(),markReaderIndex(),resetReaderIndex()这些方法能够在这里实现是因为它们和底层的读写没有关系)

AbstractByteBuf通过对这些接口对外能够提供丰富的功能,这里是不是对抽象有了深一点的理解呢?抽象出接口,并实现一部分功能,具体的实现由方法里面的子类实现的方法去实现。

wirteByte方法也是一样的:

  1. @Override
  2. public ByteBuf writeByte(int value) {
  3. ensureAccessible();
  4. ensureWritable0(1);
  5. _setByte(writerIndex++, value);
  6. return this;
  7. }

后面的方法,自行查看源码,不赘述了。

关于ByteBuf的分类,请听下回分解

发表评论

表情:
评论列表 (有 0 条评论,477人围观)

还没有评论,来说两句吧...

相关阅读

    相关 netty阅读ByteBuf

    今天我们开启新的篇章,netty很重要的内存概念将在这一章介绍。ByteBuf主要介绍以下几点: 1、内存与内存管理器的抽象 2、不同规格大小和不同类别的内存的分配策略