Liunx IO模型
1、阻塞IO模型
2、非阻塞IO模型
3、IO複用
4、信號驅動式IO
5、異步IO
Liunx IO流程
等待數據準備好
從內核向進程複製數據
阻塞IO模式
非阻塞式IO模型
複用IO模型
信號驅動式I/O模型
異步I/O模型
各種I/O模型的比較
同步、異步
同步I/O操作:導致請求進程阻塞,直到I/O操作完成。
異步I/O操作:不導致請求進程阻塞。
Blocking I/O
package demo07;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class NnServer {
private int port;
public NnServer(int port) {
this.port = port;
}
public void startServer() {
ServerSocket echoServer = null;
int i = 0;
System.out.println("服務器在端口[" + this.port + "]等待客戶請求......");
try {
echoServer = new ServerSocket(this.port);
while (true) {
Socket clientRequest = echoServer.accept();
handleRequest(clientRequest, i++);
}
} catch (IOException e) {
System.out.println(e);
}
}
private void handleRequest(Socket clientSocket, int clientNo) {
PrintStream os = null;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream());
String inputLine;
while ((inputLine = in.readLine()) != null) {
// 輸入'Quit'退出
if (inputLine.equals("Quit")) {
System.out.println("關閉與客戶端[" + clientNo + "]......" + clientNo);
os.close();
in.close();
clientSocket.close();
break;
} else {
System.out.println("來自客戶端[" + clientNo + "]的輸入: [" + inputLine + "]!");
os.println("來自服務器端的響應:" + inputLine);
}
}
} catch (IOException e) {
System.out.println("Stream closed");
}
}
public static void main(String[] args) throws IOException {
new NnServer(8080).startServer();
}
}
package demo07;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class EchoClient {
public static void main(String[] args) {
Socket echoSocket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
echoSocket = new Socket("127.0.0.1", 8080);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
echoSocket.getInputStream()));
System.out.println("連接到服務器......");
System.out.println("請輸入消息[輸入\"Quit\"]退出:");
BufferedReader stdIn = new BufferedReader(new InputStreamReader(
System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println(in.readLine());
if (userInput.equals("Quit")) {
System.out.println("關閉客戶端......");
out.close();
in.close();
stdIn.close();
echoSocket.close();
System.exit(1);
}
System.out.println("請輸入消息[輸入\"Quit\"]退出:");
}
} catch (UnknownHostException e) {
System.err.println("Don't know about host: PallaviÕs MacBook Pro.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for "
+ "the connection to: PallaviÕs MacBook Pro.");
System.exit(1);
}
}
}
NIO
NIO = New I/O
NIO 1: JSR 51
JDK 1.4引入
http://jcp.org/en/jsr/detail?id=051
NIO 2: JSR203
JDK 7
http://jcp.org/en/jsr/detail?id=203
NIO
Buffers
Channels
Selectors
NIO 2.0
Update
New File System API
Asynchronous I/O
NIO Buffer
一個 Buffer 本質上是內存中的一塊, 可以將數據寫入這塊內存, 從這塊內存獲取數據
java.nio 定義了以下幾個 Buffer 的實現
Java NIO Buffer三大核心概念:position、limit、capacity
最好理解的當然是 capacity,它代表這個緩衝區的容量,一旦設定就不可以更改。比如 capacity 爲 1024 的 IntBuffer,代表其一次可以存放 1024 個 int 類型的值。
一旦 Buffer 的容量達到 capacity,需要清空 Buffer,才能重新寫入值。
從寫操作模式到讀操作模式切換的時候(flip),position 都會歸零,這樣就可以從頭開始讀寫了。
寫操作模式下,limit 代表的是最大能寫入的數據,這個時候 limit 等於 capacity。
寫結束後,切換到讀模式,此時的 limit 等於 Buffer 中實際的數據大小,因爲 Buffer 不一定被寫滿了。
java.nio.buffer,緩衝區抽象
ByteBuffer
理解capacity、limit、position、mark
0 – mark – position – limit – capacity
Direct ByteBuffer VS. non-direct ByteBuffer
Non-direct ByteBuffer
HeapByteBuffer,標準的java類
維護一份byte[]在JVM堆上
創建開銷小
Direct ByteBuffer
底層存儲在非JVM堆上,通過native代碼操作
-XX:MaxDirectMemorySize=<size>
創建開銷大
Buffer創建
1、allocate/allocateDirect方法
2、wrap方法
Buffer讀取
1、put/get方法
2、flip方法
3、mark/reset方法
4、compact方法
5、rewind/clear
Buffer複製 – 淺複製
1、duplicate方法
2、asReadOnlyBuffer方法
3、slice方法
Java NIO Channel
所有的 NIO 操作始於通道,通道是數據來源或數據寫入的目的地,主要地, java.nio 包中主要實現的以下幾個 Channel:
FileChannel:文件通道,用於文件的讀和寫
DatagramChannel:用於 UDP 連接的接收和發送
SocketChannel:把它理解爲 TCP 連接通道,簡單理解就是 TCP 客戶端
ServerSocketChannel:TCP 對應的服務端,用於監聽某個端口進來的請求
Java NIO Selector
Selector是Java NIO中的一個組件,用於檢查一個或多個NIO Channel的狀態是否處於可讀、可寫
如此可以實現單線程管理多個channels,也就是可以管理多個網絡鏈接
java.nio.channels.Selector
支持IO多路複用的抽象實體
註冊Seletable Channel
SelectionKey —— 表示Selector和被註冊的channel之間關係,一份憑證
SelectionKey 保存channel感興趣的事件
Selector.select 更新所有就緒的SelectionKey的狀態,並返回就緒的channel個數
迭代Selected Key集合並處理就緒channel
創建Selector(Creating a Selector)
Selector selector = Selector.open();
註冊Channel到Selector上(Registering Channels with the Selector)
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
register的第二個參數,這個參數是一個“關注集合”,代表關注的channel狀態,
有四種基礎類型可供監聽, 用SelectionKey中的常量表示如下:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
從Selector中選擇channel(Selecting Channels via a Selector)
一旦向Selector註冊了一個或多個channel後,就可以調用select來獲取channel
select方法會返回所有處於就緒狀態的channel
select方法具體如下:
int select()
int select(long timeout)
int selectNow()
select()方法的返回值是一個int,代表有多少channel處於就緒了。也就是自上一次select後有多少channel進入就緒。
selectedKeys()
在調用select並返回了有channel就緒之後,可以通過選中的key集合來獲取channel,這個操作通過調用selectedKeys()方法:
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
NIO帶來了什麼
事件驅動模型
避免多線程
單線程處理多任務
非阻塞IO,IO讀寫不再阻塞,而是返回0
基於block的傳輸,通常比基於流的傳輸更高效
更高級的IO函數,zero-copy
IO多路複用大大提高了java網絡應用的可伸縮性和實用性
使用NIO = 高性能
NIO不一定更快的場景
客戶端應用
連接數<1000
併發程度不高
局域網環境下
NIO完全屏蔽了平臺差異(Linux poll/select/epoll, FreeBSD Kqueue)
NIO仍然是基於各個OS平臺的IO系統實現的,差異仍然存在
使用NIO做網絡編程很容易
離散的事件驅動模型,編程困難
陷阱重重