本章介紹
- ByteBuf
- ByteBufHolder
- ByteBufAllocator
- 使用這些接口分配緩衝和執行操作
5.1 Buffer API
- ByteBuf
- ByteBufHolder
- 可以自定義緩衝類型
- 通過一個內置的複合緩衝類型實現零拷貝
- 擴展性好,比如StringBuffer
- 不需要調用flip()來切換讀/寫模式
- 讀取和寫入索引分開
- 方法鏈
- 引用計數
- Pooling(池)
5.2 ByteBuf - 字節數據容器
5.2.1 ByteBuf如何在工作?
5.2.2 不同類型的ByteBuf
ByteBuf directBuf = Unpooled.directBuffer(16);
if(!directBuf.hasArray()){
int len = directBuf.readableBytes();
byte[] arr = new byte[len];
directBuf.getBytes(0, arr);
}
訪問直接緩衝區的數據數組需要更多的編碼和更復雜的操作,建議若需要在數組訪問數據使用堆緩衝區會更好。 CompositeByteBuf compBuf = Unpooled.compositeBuffer();
ByteBuf heapBuf = Unpooled.buffer(8);
ByteBuf directBuf = Unpooled.directBuffer(16);
//添加ByteBuf到CompositeByteBuf
compBuf.addComponents(heapBuf,directBuf);
//刪除第一個ByteBuf
compBuf.removeComponent(0);
Iterator<ByteBuf> iter = compBuf.iterator();
while(iter.hasNext()){
System.out.println(iter.next().toString());
}
//使用數組訪問數據
if(!compBuf.hasArray()){
int len = compBuf.readableBytes();
byte[] arr = new byte[len];
compBuf.getBytes(0, arr);
}
CompositeByteBuf是ByteBuf的子類,我們可以像操作BytBuf一樣操作CompositeByteBuf。並且Netty優化套接字讀寫的操作是儘可能的使用CompositeByteBuf來做的,使用CompositeByteBuf不會操作內存泄露問題。5.3 ByteBuf的字節操作
5.3.1 隨機訪問索引
ByteBuf使用zero-based-indexing(從0開始的索引),第一個字節的索引是0,最後一個字節的索引是ByteBuf的capacity - 1,下面代碼是遍歷ByteBuf的所有字節: //create a ByteBuf of capacity is 16
ByteBuf buf = Unpooled.buffer(16);
//write data to buf
for(int i=0;i<16;i++){
buf.writeByte(i+1);
}
//read data from buf
for(int i=0;i<buf.capacity();i++){
System.out.println(buf.getByte(i));
}
注意通過索引訪問時不會推進讀索引和寫索引,我們可以通過ByteBuf的readerIndex()或writerIndex()來分別推進讀索引或寫索引。5.3.2 順序訪問索引
5.3.3 Discardable bytes廢棄字節
我們可以調用ByteBuf.discardReadBytes()來回收已經讀取過的字節,discardReadBytes()將丟棄從索引0到readerIndex之間的字節。調用discardReadBytes()方法後會變成如下圖:5.3.4 可讀字節(實際內容)
ByteBuf buf = Unpooled.buffer(16);
while(buf.isReadable()){
System.out.println(buf.readByte());
}
(代碼於原書中有出入,原書可能是基於Netty4之前的版本講解的,此處基於Netty4)5.3.5 可寫字節Writable bytes
任何寫的操作會增加writerIndex。若寫操作的參數也是一個ByteBuf並且沒有指定數據源索引,那麼指定緩衝區的readerIndex也會一起增加。若沒有足夠的可寫字節會拋出IndexOutOfBoundException。新分配的緩衝區writerIndex的默認值是0。下面代碼顯示了隨機一個int數字來填充緩衝區,直到緩衝區空間耗盡: Random random = new Random();
ByteBuf buf = Unpooled.buffer(16);
while(buf.writableBytes() >= 4){
buf.writeInt(random.nextInt());
}
5.3.6 清除緩衝區索引Clearing the buffer indexs
5.3.7 搜索操作Search operations
5.3.8 標準和重置Mark and reset
5.3.9 衍生的緩衝區Derived buffers
// get a Charset of UTF-8
Charset utf8 = Charset.forName("UTF-8");
// get a ByteBuf
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
// slice
ByteBuf sliced = buf.slice(0, 14);
// copy
ByteBuf copy = buf.copy(0, 14);
// print "Netty in Action rocks!"
System.out.println(buf.toString(utf8));
// print "Netty in Act"
System.out.println(sliced.toString(utf8));
// print "Netty in Act"
System.out.println(copy.toString(utf8));
5.3.10 讀/寫操作以及其他一些操作
- get/set操作以索引爲基礎,在給定的索引設置或獲取字節
- 從當前索引開始讀寫,遞增當前的寫索引或讀索引
5.4 ByteBufHolder
ByteBufHolder是一個輔助類,是一個接口,其實現類是DefaultByteBufHolder,還有一些實現了ByteBufHolder接口的其他接口類。ByteBufHolder的作用就是幫助更方便的訪問ByteBuf中的數據,當緩衝區沒用了後,可以使用這個輔助類釋放資源。ByteBufHolder很簡單,提供的可供訪問的方法也很少。如果你想實現一個“消息對象”有效負載存儲在ByteBuf,使用ByteBufHolder是一個好主意。5.4.1 ByteBufAllocator
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// get ByteBufAllocator instance by Channel.alloc()
ByteBufAllocator alloc0 = ch.alloc();
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//get ByteBufAllocator instance by ChannelHandlerContext.alloc()
ByteBufAllocator alloc1 = ctx.alloc();
ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
}
});
}
});
Netty有兩種不同的ByteBufAllocator實現,一個實現ByteBuf實例池將分配和回收成本以及內存使用降到最低;另一種實現是每次使用都創建一個新的ByteBuf實例。Netty默認使用PooledByteBufAllocator,我們可以通過ChannelConfig或通過引導設置一個不同的實現來改變。更多細節在後面講述。5.4.2 Unpooled
//創建複合緩衝區
CompositeByteBuf compBuf = Unpooled.compositeBuffer();
//創建堆緩衝區
ByteBuf heapBuf = Unpooled.buffer(8);
//創建直接緩衝區
ByteBuf directBuf = Unpooled.directBuffer(16);