`

netty学习笔记1--ByteBuf未完待续

阅读更多

       netty中的ByteBuf是基于java.nio的ByteBuffer扩展的,主要是因为nio中的ByteBuffer中存在一些使用上的不方便,比如:1.创建的ByteBuffer对象是固定容量的,当超过容量便会报错。2.只有一个标识位置的指针,读写数据的时候需要手动调用flip()和rewind()方法,使用稍有不慎便会造成程序报错。当然在一些官方文档上面关于这个的描述还有其他一些问题,这里就不再详述了。

       接下来讲讲netty中的ByteBuf吧,看看这个是如何解决上面存在的问题以及又有哪些拓展的功能呢?

       1.ByteBuf的工作原理:

        首先ByteBuf依然是byte数组的缓冲区,它的基本功能同jdk的ByteBuffer,如下:

(1)7种java基础类型,byte数组,ByteBuffer(ByteBuf)等的读写;

(2)缓冲区自身的copy(拷贝)与slice(截取)等;

(3)设置网络字节序;

(4)构造缓冲区实例;

(5)操作位置指针等方法。

 以上的功能具体对应ByteBuf中的哪些方法容以后在来补充吧。

 ByteBuf通过两个位置指针来协助缓冲区的读写操作,读操作使用readerIndex,写操作使用writerIndex,readerIndex与writerIndex一开始都是0,随着数据的写入writerIndex会增加,读取数据会使readerIndex增加,但是readerIndex永远小于writerIndex。在读取数据后,0~readerIndex之间的数据就被视为discard,调用discardReadBytes方法可以释放这部分的空间,这个作用就有点像nio ByteBuffer中的compact方法。另外readerIndex与writerIndex方法之间的数据是可读的,等价于nio ByteBuffer中的position和limit之间的数据。writerIndex与capacity之间的空间是可写的,等价于ByteBuffer中的limit和capacity之间的可用空间。

 由于读操作不修改writerIndex的值,写操作修改readerIndex的值,这样读写之间就不需要调整位置指针,这极大的简化了缓冲区的读写操作,避免由于遗漏或者不熟悉flip操作导致的功能异常。

 接下来继续讨论ByteBuf如何实现动态扩展的。通常情况下,当我们对ByteBuffer进行put操作的时候,都要校验当前ByteBuffer的可写空间是否足够,如果不够,需要将当前的ByteBuffer拷贝到一个更到容量的ByteBuffer中去,然后将先前的ByteBuffer释放掉,具体代码如下:

 

if(this.buffer.remaining() < needSize) {
   int toBeExtSize = needSize > 128 ? needSize : 128;
   ByteBuffer tmpBuffer = ByteBuffer.allocate(this.buffer.capacity+toBeExtSize);
   this.buffer.flip();
   tmpBuffer.put(this.buffer);
   this.buffer = tmpBuffer;
   
}

 

 

 

 

 

 从上面的代码可以看出,每进行一次put操作的时候都要进行一次可用空间的校验,这在平时的代码实现上很是麻烦,稍有不慎还有可能引发其他问题。为了解决这个问题,netty ByteBuf对nio ByteBuffer的write操作进行了封装,由这个封装后的write方法进行校验当前buffer的可用空间是否足够,并进行扩容。具体代码如下:

 @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format(
                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        }

        if (minWritableBytes <= writableBytes()) {
            return this;
        }

        if (minWritableBytes > maxCapacity - writerIndex) {
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                    writerIndex, minWritableBytes, maxCapacity, this));
        }

        // Normalize the current capacity to the power of 2.
        int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);

        // Adjust to the new capacity.
        capacity(newCapacity);
        return this;
    }

 

 @Override
    public ByteBuf writeShort(int value) {
        ensureAccessible();
        ensureWritable(2);
        _setShort(writerIndex, value);
        writerIndex += 2;
        return this;
    }

    @Override
    public ByteBuf writeMedium(int value) {
        ensureAccessible();
        ensureWritable(3);
        _setMedium(writerIndex, value);
        writerIndex += 3;
        return this;
    }

    @Override
    public ByteBuf writeInt(int value) {
        ensureAccessible();
        ensureWritable(4);
        _setInt(writerIndex, value);
        writerIndex += 4;
        return this;
    }

 从上面的代码可以看出,ByteBuf在为确保可用空间的足够,在每次进行写操作的时候都会调用一次ensureWritable(int)方法来保证不会出现容量不够的情况,这样我们在实际应用开发的时候就不需要考虑容量的问题,只需要专心在我们具体往ByteBuf中写什么东西等逻辑上面了。

前面大致总结了ByteBuf的原理,接下来看看api吧,ByteBuf具体给我们提供了那些方法供使用呢 ?

1.顺序读操作(read)

ByteBuf的read操作类似于ByteBuffer的get操作,

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics