本篇文章介紹一下java中的Nio中的Buffer,注意不是Netty中的,後面會講解Netty中的Buffer
什麼是Buffer
Buffer本質就是一個可以讀寫的內存塊,它提供了一些方法,可以輕鬆的往裏面寫數據和讀數據。
Nio中Channel提供了從文件或者網絡讀取數據的渠道,但是讀取和寫入數據時,必須經由Buffer
Nio中Buffer有以下幾種:
- IntBuffer
- FloatBuffer
- CharBuffer
- DoubleBuffer
- ShortBuffer
- LongBuffer
- ByteBuffer
可以看出八種基本數據類型除了boolean,都有一個對應的Buffer
下面以IntBuffer爲例講解一下Buffer中的方法,然後通過實例展示
public static IntBuffer allocate(int capacity)
創建一個IntBuffer對象。指定這個IntBuffer的容量
//可以放5個數字到這個buffer中 IntBuffer intBuffer = IntBuffer.allocate(5);
public abstract IntBuffer put(int i);
向IntBuffer中放一個整數//創建一個存放5個整數的buffer IntBuffer intBuffer = IntBuffer.allocate(5); //將整數1放到buffer中 intBuffer.put(1);
-
public final Buffer flip() 切換buffer的讀寫。內部源碼實際的邏輯就是通過控制position的值來進行操作的
-
public final boolean hasRemaining() 判斷buffer中是否有數據,一般在while循環中當作條件。
例子:
public class BufferTest {
public static void main(String[] args) throws Exception {
IntBuffer intBuffer = IntBuffer.allocate(5);
//intBuffer.put(1);
for (int i = 0; i < intBuffer.capacity(); i++) {
intBuffer.put(i);
}
// 如何從buffer讀數據,通過這個方法將buffer切換爲模式,然後就可以讀數據,
// 如果想往buffer裏再寫數據,就在執行一次這個方法轉換一下就行
intBuffer.flip();
// 如果intBuffer中還有數據
while (intBuffer.hasRemaining()) {
System.out.println(intBuffer.get());
}
}
}
執行結果爲:
下面通過源碼,來看一下
- buffer中的數據從在那? 在上面的實例中,我們創建了一個int的buffer,可以保存4個數據,那麼這4個數據其實就是保存在這個紅框的hb數組中的。(8個buffer都會有一個對應的hb)。
- buffer中的4個屬性的意思
屬性 描述 capacity buffer可以存放的最大數據,創建buffer是確定,然後就不可以在變了 limit 表示緩衝區的當前終點。就是設置以後,讀寫buffer時不能超過這個limit的位置 position 表示將要讀取數據的位置或者寫入數據的位置 mark 標記
position爲0表示要將數據寫入到的位置是下標爲0的位置,下一次就會變爲1以此類推。但是因爲limit爲5,則表示當position爲4時就不能再添加了。這就是limit的作用。如果現在吧limit設置爲1,那麼添加完第一數據後,就不能再添加了,即便是我們開始設置的容量capacity爲5,也不能再添加數據了,這就是limit的作用。
常用的api解釋
Nio還支持通過多個Buffer(即Buffer數組)完成讀寫操作,也就是Scattering 和Gathering
什麼是Scattering?
將數據寫入buffer時,可以依次寫入,比如一個buffer寫滿了,就往buffer數組的下一個buffer裏寫
什麼是Gathering
從buffer讀數據時,可以採用buffer數組,比如一個buffer讀完之後,就從buffer數組的下一個buffer中繼續讀。
下面通過例子看一下:
public static void main(String[] args) throws Exception {
ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
serverSocketChannel.socket().bind(inetSocketAddress);
ByteBuffer[] byteBuffers = new ByteBuffer[2];
byteBuffers[0] = ByteBuffer.allocate(5);
byteBuffers[1] = ByteBuffer.allocate(3);
SocketChannel socketChannel = serverSocketChannel.accept();
int messageLength = 8; //假定從客戶端接收8個字節
while (true) {
int byteRead = 0;
while (byteRead < messageLength) {
long l = socketChannel.read(byteBuffers);// 讀取的字節數
byteRead += l; // 累計讀取的字節數
System.out.println("byteRead=" + byteRead);
// 使用流打印,看看當前buffer的position和limit
Arrays.asList(byteBuffers).stream().map(buffer->"position:" + buffer.position() + "limit:" + buffer.limit()).forEach(System.out::println);
}
//對所有的buffer進行flip
Arrays.asList(byteBuffers).forEach(buffer->buffer.flip());
//將數據讀出,顯示到客戶端
long byteWrite = 0;
while(byteWrite < messageLength) {
long l = socketChannel.write(byteBuffers);
byteWrite += l;
}
//將所有的buffer進行clean
Arrays.asList(byteBuffers).forEach(buffer->buffer.clear());
System.out.println("byteRead=" + byteRead + "byteWrites=" + byteWrite + "messageLength=" + messageLength);
}
}
運行啓動程序後,然後通過cmd啓動telenet,方式如下:
1.win + R,然後輸入cmd
2.打開cmd後,輸入telnet 127.0.0.1 7000 連接我們的socket
3.連接成功後,輸入helloa六個字符
結果:然後可以發現helloa在填充完第一個數組後,繼續想第二個數組中填寫。然後從字節數組中讀的時候,也是把兩個數組都讀出來了。