Java NIO 系列教程(四)
阿新 • • 發佈:2019-01-24
阻塞式與非阻塞式(主要針對網路程式設計而言)
阻塞式:
比如到你某個時候到A樓一層(假如是核心緩衝區)取快遞,但是你不知道快遞什麼時候過來,你又不能幹別的事,只能死等著。非阻塞式:
還是等快遞的例子:你知道今天要來快遞,等快遞員給你打電話,在快遞員打電話之前你可以忙自己的事情,等接到電話後再過去取,這就是非阻塞式的。
為進一步提高IO的效率,使用了多執行緒來解決這個問題,但是執行緒的數量總是會因為CPU的切換到達負荷值。
選擇器是用來監控通道的I/O的狀態,等Client的資料準備就緒,就通知Server來處理,這期間Server中的執行緒可以做其他的事情。
使用NIO完成網路通訊的三個核心
1)連線Channel
2)緩衝區Buffer
3)選擇器Select:用於監控SelectbleChannel的IO狀態。
阻塞式網路通訊實現
@Test
public void Server() throws IOException {
// 獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
FileChannel fon = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE );
// 繫結埠號
ssChannel.bind(new InetSocketAddress(9989));
// 接受來自客戶端的請求
SocketChannel sChannel = ssChannel.accept();
// 建立緩衝區用於傳輸資料
ByteBuffer buf = ByteBuffer.allocate(1024);
while (sChannel.read(buf) != -1) {
buf.flip();
fon.write (buf);
buf.clear();
}
String str = new String("傳輸完成");
buf.put(str.getBytes());
buf.flip();
sChannel.write(buf);
sChannel.shutdownOutput();
ssChannel.close();
fon.close();
sChannel.close();
}
@Test
public void Client() throws IOException {
// 獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9989));
// 關聯檔案,將檔案資料傳送到客戶端,並且儲存接收來自客戶端的資料
FileChannel fin = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ, StandardOpenOption.WRITE);
// 利用緩衝區儲存資料
ByteBuffer buf = ByteBuffer.allocate(1024);
// 利用通道傳輸資料
while (fin.read(buf) != -1) {
buf.flip();
sChannel.write(buf);
buf.clear();
}
int length = 0;
while ((length = sChannel.read(buf)) != -1) {
buf.flip();
System.out.println(new String(buf.array(), 0, length));
buf.clear();
}
sChannel.shutdownOutput();
sChannel.close();
fin.close();
}
非阻塞式實現
@Test
public void Client() throws IOException {
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
// 切換為非阻塞式的
sChannel.configureBlocking(false);
// 申請緩衝區
ByteBuffer buf = ByteBuffer.allocate(1024);
// 寫入日期
buf.put(new Date().toString().getBytes());
buf.flip();
sChannel.write(buf);
// 關閉緩衝區
sChannel.close();
}
@Test
public void Server() throws IOException {
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 繫結埠號
ssChannel.bind(new InetSocketAddress(9988));
// 切換為非阻塞式
ssChannel.configureBlocking(false);
// 構造選擇器
Selector selector = Selector.open();
// 將通道註冊到相應的選擇器上(後面的引數表示監控的通道的狀態:包含讀、寫、連線、接收)
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
// 通過選擇器輪迴的方式獲取選擇器上已經準備就緒的事件
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
SocketChannel scChannel = ssChannel.accept();
scChannel.configureBlocking(false);
// 監控讀就緒狀態
scChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 獲取當前選擇器上“讀就緒狀態”的通道
SocketChannel sChannel = (SocketChannel) selectionKey.channel();
// 讀取資料
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while ((len = sChannel.read(buf)) != -1) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
}
// 取消選擇鍵
iterator.remove();
}
}
}