《精通併發與Netty》學習筆記(12 - 詳解NIO (三) SocketChannel、Pipe)

1、SocketChannel
Java NIO中的SocketChannel是一個鏈接到TCP網絡套接字的通道。能夠經過如下2種方式建立SocketChannel:服務器

打開一個SocketChannel並鏈接到互聯網上的某臺服務器。
一個新鏈接到達ServerSocketChannel時,會建立一個SocketChannel。網絡

打開 SocketChannel
下面是SocketChannel的打開方式:異步

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

關閉 SocketChannelsocket

當用完SocketChannel以後調用SocketChannel.close()關閉SocketChannel:spa

socketChannel.close();

從 SocketChannel 讀取數據線程

要從SocketChannel中讀取數據,調用一個read()的方法之一。如下是例子:code

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);

首先,分配一個Buffer。從SocketChannel讀取到的數據將會放到這個Buffer中。blog

而後,調用SocketChannel.read()。該方法將數據從SocketChannel 讀到Buffer中。read()方法返回的int值表示讀了多少字節進Buffer裏。若是返回的是-1,表示已經讀到了流的末尾(鏈接關閉了)。ip

寫入 SocketChannel
寫數據到SocketChannel用的是SocketChannel.write()方法,該方法以一個Buffer做爲參數。示例以下:get

String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}

注意SocketChannel.write()方法的調用是在一個while循環中的。Write()方法沒法保證能寫多少字節到SocketChannel。因此,咱們重複調用write()直到Buffer沒有要寫的字節爲止。

非阻塞模式
能夠設置 SocketChannel 爲非阻塞模式(non-blocking mode).設置以後,就能夠在異步模式下調用connect(), read() 和write()了。

connect()
若是SocketChannel在非阻塞模式下,此時調用connect(),該方法可能在鏈接創建以前就返回了。爲了肯定鏈接是否創建,能夠調用finishConnect()的方法。像這樣:

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

while(! socketChannel.finishConnect() ){
//wait, or do something else...
}

write()

非阻塞模式下,write()方法在還沒有寫出任何內容時可能就返回了。因此須要在循環中調用write()。前面已經有例子了,這裏就不贅述了。

read()
非阻塞模式下,read()方法在還沒有讀取到任何數據時可能就返回了。因此須要關注它的int返回值,它會告訴你讀取了多少字節。

非阻塞模式與選擇器
非阻塞模式與選擇器搭配會工做的更好,經過將一或多個SocketChannel註冊到Selector,能夠詢問選擇器哪一個通道已經準備好了讀取,寫入等

2、Java NIO DatagramChannel
Java NIO中的DatagramChannel是一個能收發UDP包的通道。由於UDP是無鏈接的網絡協議,因此不能像其它通道那樣讀取和寫入。它發送和接收的是數據包。

打開 DatagramChannel
下面是 DatagramChannel 的打開方式:

DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));

這個例子打開的 DatagramChannel能夠在UDP端口9999上接收數據包。

接收數據
經過receive()方法從DatagramChannel接收數據,如:

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);

receive()方法會將接收到的數據包內容複製到指定的Buffer. 若是Buffer容不下收到的數據,多出的數據將被丟棄。

發送數據
經過send()方法從DatagramChannel發送數據,如:

String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));

這個例子發送一串字符到」jenkov.com」服務器的UDP端口80。 由於服務端並無監控這個端口,因此什麼也不會發生。也不會通知你發出的數據包是否已收到,由於UDP在數據傳送方面沒有任何保證。

鏈接到特定的地址
能夠將DatagramChannel「鏈接」到網絡中的特定地址的。因爲UDP是無鏈接的,鏈接到特定地址並不會像TCP通道那樣建立一個真正的鏈接。而是鎖住DatagramChannel ,讓其只能從特定地址收發數據。

這裏有個例子:

channel.connect(new InetSocketAddress("jenkov.com", 80));

當鏈接後,也可使用read()和write()方法,就像在用傳統的通道同樣。只是在數據傳送方面沒有任何保證。這裏有幾個例子:

int bytesRead = channel.read(buf);
int bytesWritten = channel.write(but);

3、Pipe

Java NIO 管道是2個線程之間的單向數據鏈接。Pipe有一個source通道和一個sink通道。數據會被寫到sink通道,從source通道讀取。

這裏是Pipe原理的圖示:

 


建立管道
經過Pipe.open()方法打開管道。例如:

Pipe pipe = Pipe.open();

向管道寫數據

要向管道寫數據,須要訪問sink通道。像這樣:

Pipe.SinkChannel sinkChannel = pipe.sink();

經過調用SinkChannel的write()方法,將數據寫入SinkChannel,像這樣:

String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
sinkChannel.write(buf);
}

從管道讀取數據

從讀取管道的數據,須要訪問source通道,像這樣:

Pipe.SourceChannel sourceChannel = pipe.source();

調用source通道的read()方法來讀取數據,像這樣:

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = sourceChannel.read(buf);

read()方法返回的int值會告訴咱們多少字節被讀進了緩衝區。

相關文章
相關標籤/搜索