您的位置:首页 > 其它

Netty in Action (十三) 第五章节 第二部分 ByteBuf字节层面的操作

2016-04-24 11:32 483 查看
5.3 Byte-level operations

ByteBuf除了提供基本对数据读写操作之外,它还提供了很多其他的方法,在接下来的这个小节中,我们将讨论这些方法中比较重要的来分析讲解一下

5.3.1 Random access indexing

与正常的java的字节数组一样,ByteBuf的索引下标也是从0开始的,第一个索引下表是0,最后一个字节索引总是它的容量-1,下面的代码清单向你展示了ByteBuf封装了它的存储机制,可以使我们很方便地去迭代ByteBuf中的内容



注意到如果想获取ByteBuf中的数据,使用将索引下标当做一个参数传递的时候,不会修改readerIndex和writerIndex的索引值,除非有需要可以手动调用readerIndex和writerIndex的方法来修改对应的值

5.3.2 Sequential access indexing

ByteBuf中既有读索引也有写索引,但是JDK中的ByteBuffer只有一个索引,这就是为什么你需要调用flip的方法来切换读写模式的切换,图5.3向你展示了ByteBuf被这两个索引被切分成三个部分的示例图



5.3.3
Discardable bytes

在上图5.3中标注“Discardable bytes”的段结构包含的是已经被读取过的字节,这段空间可以通过调用discardReadByte方法被回收,然后就可以获取到内存再利用的效果,这段空间的初始化大小被存储在readerIndex中,是0,它的真实大小将随着read的操作逐渐递增(以get开头的方法不会移动readerIndex的下标值)

图5.4向你展示图5.3中通过调用discardReadByte()方法后buffer空间修改后的样子,你可以看见以前空间中可以被回收的段已经可以被用来写了,注意一点在调用discardReadByte的方法之后,可写的段的内容并没有得到保证



如果你经常调用discardReadByte的方法来保证可写段的最大容量,请你意识到这可能会引起内存复制,这是因为上图中标注“CONTENT”的可读内容需要移动到ByteBuf的头部去,所以我们建议你除非真的有需要才来调用discardReadByte来增加可读的空间,例如,内存真的很吃紧

5.3.4 Readable bytes

可读段保证了真正的数据,一个新分配或者等待被覆盖,复制的初始化好的ByteBuf的默认初始下标是0,任何以”read“和”skip“名字开头的方法来操作ByteBuf都会先获取当前数组中下标的字节然后跳过该字节,且readerIndex的值+1,按照这个规律,一直读取下一个

当有一个方法以ByteBuf作为参数当做一个写的目标,并且没有一个目标索引参数,那么目标buffer的写索引也会改变,例如:



当可读的段已经被读完的时候,还尝试接着读取的时候,会发生IndexOutOfBoundsException的异常

下面的代码清单教你如何正确的读取可读的字节



5.3.5
Writable bytes

可写段的内存区域中是没有定义任何东西的,这些区域等待被写,一个新的被分配的buffer的默认writerIndex的索引值是0,如果一个操作的名字是“write”开头的被执行的时候,就意味着开始从当前写索引下标开始写数据了,而且该索引下标的值会随着你字节数量的增加而增加,同样,如果一个写的操作对象依旧是ByteBuf并且也没有指定源索引值,那么该buffer的readerIndex也会随着增加,例如:



如果可写的空间已经用完的时候还在尝试接着写入,会发生IndexOutOfBoundException的异常

下面的代码清单中给出了一个示例,如何在保证容量足够的情况下,随机的写入一个Integer类型的值,方法writableBytes在这里被用来确定是否还有足够的空间被用来读



5.3.6 Index management

JDK的InputStream定义了mark和reset两个方法,这些方法用来标注当前的stream的索引到的具体位置,然后可以相对应的将stream重置到刚才标注的位置

相同的,你可以通过调用markReaderIndex,markWriterIndex,resetReaderIndex,resetWriteIndex这些方法来设置和重新定位ByteBuf的readerIndex和writeIndex的值,这与InputStream的调用很是相似,当没有具体的readerLimit参数指定的时候,这个标注就是非法的

你可以通过调用readerIndex(Int)和writeIndex(int)两个方法来移动两个指针,当然如果你尝试移动这两个类型的索引下标到不存在的位置会报出IndexOutOfBoundsException的异常

你也可以通过调用clear的方法将readerIndex和writeIndex两个索引下标全部置为0,注意这将不会讲内存中内容给清除掉,图5.5向你展示了clear的工作原理



图5.5向你展示了clear方法执行之前的样子,ByteBuf包含3个部分,图5.6向你展示了在clear方法调用之后的ByteBuf的样子



调用clear比调用discardReadBytes方法更加经济,因为它只是重置了两个索引的位置到0并没有复制任何内存

5.3.7 Search operations

有几个方法可以确定ByteBuf的一些具体的值,最简单的方法就是indexOf方法了,还有一些检索方法通过将一个ByteBufProcessor类型的数据作为参数可以去更加复杂的检索,ByteBufProcessor是一个接口,里面有一个简单的方法



这个方法的意思是输入的字节是否可以被寻求到

ByteBufProcessor中还定义了很多目标常量,假如你的应用与Flash的socket有交互的话,这里面的null为结尾的内容,可以调用



获取Flash中的数据还是很简单高效的,因为在处理过程中只绑定了很少的一些校验检查

下面的代码清单向你展示了如何寻找(\r)这样不常见的字符



5.3.8 Derived buffers

衍生的buffer提供了ByteBuf以一种特殊的方式去展示它所含有的内容,一些特殊的视图可以通过如下的一些方式去创建



这里面的每一个方法都会返回一个新的ByteBuf的实例且这个实例有它自己的读写标记索引,内部存储与JDK的ByteBuffer一致,创建一个衍生的buffer的代价并不是很高,因为没有复制,这也就意味着如果你修改衍生buffer的值同时也就在修改它的原生值

BYTEBUF COPYING 如果你想完全复制一个已经存在的buffer,使用copy和copy(int,int)方法,与衍生buffer不一样的地方是:copy返回的是一个全新的独立的ByteBuf与原Buf没有任何联系

下面代码清单向你展示了一个ByteBuf的段使用slice后是如何工作的



我们再看看ByteBuf段copy方法与slice方法不一样的地方



这两个用例是很相似的,除了对源数据的产生的作用是不一样的,如果有可能,尽可能地使用slice来减少内存的拷贝

5.3.9 Read/write operations

在我们之前提过,有对于读写而言分为两种大的模式
1)get()和set()方法将从给定的索引下标开始操作,但是操作过,对应的下标并不会改变
2)read()和write()方法会从给定的索引下标开始操作,但也会根据操作对应的调整数据的值

表5.1中向你展示了常用的一些get的方法,全部的方法清单可以查看java的文档



对应的常用的set方法如5.2表所示:



下面的代码清单向你展示了get和set方法的用法,这两种方法并没有改变读和写的索引值



我们再来看看read方法对readerIndex或者对writeIndex的作用,这个方法可以将ByteBuf当做一个stream来从这里面读取数据,表5.3展示了read方法族中最最常用的一些方法





几乎所有的read方法都会有一个write方法与之对应,用来在ByteBuf写入字节,注意一下所有的方法的入参指的是写入的值,而不是索引值



代码清单5.13向你展示了这些方法如何使用



5.3.10 More operations

表5.5展示了ByteBuf提供的一些其他的额外有用的方法

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  netty