上一篇文章BIO、NIO扫盲主要介绍了BIO和NIO模型网络结构,并通过简单代码说明BIO和NIO核心功能及使用。NIO通过多路复用选择器Selector解耦server和client的网络连接,只有当client对应的channel准备好相关事件后,server端才会作出相应回馈,通过这种机制支持网络高并发。但使用NIO类库和API繁杂,要考虑线程安全、失败缓存、网络闭包等问题,工作量和难度都很大。Netty是业界最流行的NIO框架之一,经历大规模商业应用考验,成熟、稳定、性能高。接下来介绍下NIO网络通信中java nio ByteBuffer和Netty中ByteBuf中主要方法属性。
ByteBuffer
Java NIO提供ByteBuffer作为字节容器,但只有一个位置指针用于处理读写操作,每次读写时候都需要额外调用flip()和clear()方法,否则功能将会出错。查看ByteBuffer源码如下:
mark:调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值;
position:是下一个要被读写的元素的数组下标索引,该值会随get()和put()的调用自动更新;
limit:是缓冲区中第一个不能读写的元素的数组下标索引,也可以认为是缓冲区中实际元素的数量;
capacity:是缓冲区能够容纳元素的最大数量,这个值在缓冲区创建时被设定,而且不能够改变,如下,创建了一个最大容量为10的字节缓冲区;
四者关系如下:
0 <=mark <= position <= limit <= capacity
具体功能展示:
创建ButterBuffer:
ByteBuffer bf = ByteBuffer.allocate(10);
创建初始化缓冲区,可以看到初始值position=0,而limit=capacity=初始化长度
往ByteBuffer灌入数据
bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'0')
向缓冲区存入数据时,没存储一个字符,position索引后移一位
调用flit刷新缓冲区为读模式
bf.flip()
缓冲区有两种模式,分别是写模式和读模式,这两种模式通过使用flip方法进行模式。如上将缓冲区切换为读模式,则position变成了初值位置0,而limit变成了写模式下position位置。
读取缓冲区中到内容:get()
System.out.println((char)bf.get()+""+(char)bf.get());
调用mark(),标记position位置
bf.mark()
标记完position后,继续读取缓冲区区内容
System.out.print((char)bf.get()+""+(char)bf.get())
使用reset()方法可以使position回到mark存储到位置
bf.reset()
调用compact()方法,清空已读取数据空间,进行数据到重新填充
bf.compact()
compact方法当缓冲区数据写入channel时没有一次性写入完成,通过执行compact方法,将没有写完的数据重新移动到缓冲区到初始位置,position调整为没有写完数据的下个索引,limit调整为capacity。这样buffer可以循环使用,继续向buffer写入数据了。
使用clear清空缓冲区
bf.clear()
ByteBuf
ByteBuf是Netty框架封装的数据缓冲区,区别于ByteBuffer需要有position、limit、flip等属性和操作来控制byteBuffer数据读写,Bytebuf通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writerIndex。
初始化ByteBuf时,readIndex和writerIndex取值一开始是0,随着数据的写入writerIndex会增加,读取数据会使readIndex增加,但不会超过writerIndex。在读取之后0-readIndex被视为discard,调用discardReadBytes方法,释放这部分空间,作用类似于ByteBuffer的compact方法,移除无用数据,实现缓冲区的重复使用。
初始化ByteBuf
ByteBuf bf= Unpooled.buffer(10,100)
write:写入N个字节之后ByteBuf
read:读取M个字节后(M<N)
discardReadBytes:
总结
在上面的介绍中可以看到Netty中的ByteBuf使用更加方便,还提供了查找操作(indexOf(int fromindex,int toIndex,byte value)、bytesBefore(Byte value))、数据复制(Derived buffers)、ByteBuffer和ByteBuf互相转化等功能更强大的方法。