1. 程式人生 > >BIO與NIO比較及例子

BIO與NIO比較及例子

java NIO的工作原理:
1. 由一個專門的執行緒來處理所有的 IO 事件,並負責分發。 
2. 事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。 
3. 執行緒通訊:執行緒之間通過 wait,notify 等方式通訊。保證每次上下文切換都是有意義的。減少無謂的執行緒切換。

如下圖:一個執行緒Reactor用來處理所有io,並分發read、write等事件

                                             (本圖來自網際網路)

三:結合網際網路的例子進行分析NIO:

1、先看例子的原始碼,不妨debug除錯下

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
	
	/*標識數字*/
	private  int flag = 0;
	/*緩衝區大小*/
	private  int BLOCK = 4096;
	/*接受資料緩衝區*/
	private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
	/*傳送資料緩衝區*/
	private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
	private  Selector selector;

	public NIOServer(int port) throws IOException {
		// 1、開啟伺服器套接字通道
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 伺服器配置為非阻塞
		serverSocketChannel.configureBlocking(false);
		// 檢索與此通道關聯的伺服器套接字
		ServerSocket serverSocket = serverSocketChannel.socket();
		//2、 進行服務的繫結
		serverSocket.bind(new InetSocketAddress(port));
		//3、 通過open()方法找到Selector
		selector = Selector.open();
		//4、註冊到selector,等待連線
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("Server Start----8888:");
	}


	// 監聽
	private void listen() throws IOException {
		while (true) {
			// 選擇一組鍵,並且相應的通道已經開啟
			selector.select();
			// 返回此選擇器的已選擇鍵集。
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectionKeys.iterator();
			//5 輪詢就緒的key
			while (iterator.hasNext()) {		
				SelectionKey selectionKey = iterator.next();
				iterator.remove();
				handleKey(selectionKey);
			}
		}
	}

	// 處理請求
	private void handleKey(SelectionKey selectionKey) throws IOException {
		// 接受請求
		ServerSocketChannel server = null;
		SocketChannel client = null;
		String receiveText;
		String sendText;
		int count=0;
		// 測試此鍵的通道是否已準備好接受新的套接字連線。
		//步驟6 handle connect 處理新的客戶接入
		if (selectionKey.isAcceptable()) {
			// 返回為之建立此鍵的通道。
			//步驟7 設定新建連線的socket
			server = (ServerSocketChannel) selectionKey.channel();
			// 接受到此通道套接字的連線。
			// 此方法返回的套接字通道(如果有)將處於阻塞模式。
			client = server.accept();
			// 配置為非阻塞
			client.configureBlocking(false);
			//步驟8   註冊到selector,等待連線
			client.register(selector, SelectionKey.OP_READ);
		} else if (selectionKey.isReadable()) {//步驟9:非同步處理請求訊息到ByteBuffer(),程式碼中沒有步驟10
			// 返回為之建立此鍵的通道。
			client = (SocketChannel) selectionKey.channel();
			//將緩衝區清空以備下次讀取
			receivebuffer.clear();
			//讀取伺服器傳送來的資料到緩衝區中
			count = client.read(receivebuffer);	
			if (count > 0) {
				receivebuffer.flip();
				byte[] bytes = new byte[receivebuffer.remaining()];
				receivebuffer.get(bytes);
				receiveText = new String(bytes,"utf-8");
				System.out.println("伺服器端接受客戶端資料--:"+receiveText);
				client.register(selector, SelectionKey.OP_WRITE);
			}
		} else if (selectionKey.isWritable()) {//步驟11 非同步寫
			//將緩衝區清空以備下次寫入
			sendbuffer.clear();
			// 返回為之建立此鍵的通道。
			client = (SocketChannel) selectionKey.channel();
			sendText="message from server--" + flag++;
			//向緩衝區中輸入資料
			sendbuffer.put(sendText.getBytes());
			 //將緩衝區各標誌復位,因為向裡面put了資料標誌被改變要想從中讀取資料發向伺服器,就要復位
			sendbuffer.flip();
			//輸出到通道
			client.write(sendbuffer);
			System.out.println("伺服器端向客戶端傳送資料--:"+sendText);
			client.register(selector, SelectionKey.OP_READ);
		}
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		int port = 8888;
		NIOServer server = new NIOServer(port);
		server.listen();
	}
}

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOClient {

	/*標識數字*/
	private static int flag = 0;
	/*緩衝區大小*/
	private static int BLOCK = 4096;
	/*接受資料緩衝區*/
	private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
	/*傳送資料緩衝區*/
	private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
	/*伺服器端地址*/
	private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
			"localhost", 8888);

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		//步驟1  開啟socket的通道socketChannel
		SocketChannel socketChannel = SocketChannel.open();
		//步驟2  設定為非阻塞方式,同時設定tcp引數
		socketChannel.configureBlocking(false);
		Selector selector = null;
		
		
		// 非同步連線伺服器
		if (socketChannel.connect(SERVER_ADDRESS)) {
			
		}
		else {
			//步驟5  註冊連線服務端socket動作
			selector = Selector.open();
	        socketChannel.register(selector, SelectionKey.OP_CONNECT);
		}	
		// 分配緩衝區大小記憶體
		
		
		Set<SelectionKey> selectionKeys;
		Iterator<SelectionKey> iterator;
		SelectionKey selectionKey;
		SocketChannel client;
		String receiveText;
		String sendText;
		int count=0;
               //步驟6 啟動執行緒
		while (true) {
			//選擇一組鍵,其相應的通道已為 I/O 操作準備就緒。
			//此方法執行處於阻塞模式的選擇操作。
			int ret = selector.select();
			//System.out.println(ret);
			
			//返回此選擇器的已選擇鍵集。
			selectionKeys = selector.selectedKeys();
			//System.out.println(selectionKeys.size());
			iterator = selectionKeys.iterator();
			while (iterator.hasNext()) {//7 輪詢就緒的key
				selectionKey = iterator.next();
				//4  判斷連線結果,如果連線成功,跳到步驟10,如果不成功,執行步驟5
				if (selectionKey.isConnectable()) {
					System.out.println("client connect");
					client = (SocketChannel) selectionKey.channel();
					// 判斷此通道上是否正在進行連線操作。
					// 完成套接字通道的連線過程。8 handle connect()
					if (client.isConnectionPending()) {
						client.finishConnect();//9 判斷連線完成,完成連線
						System.out.println("完成連線!");
						sendbuffer.clear();
						sendbuffer.put("Hello,Server".getBytes());
						sendbuffer.flip();
						client.write(sendbuffer);
					}
					//步驟10 向多路複用器註冊 OP_READ
					client.register(selector, SelectionKey.OP_READ);
				} else if (selectionKey.isReadable()) {//步驟11 handle read() 非同步讀請求訊息到ByteBuffer
					client = (SocketChannel) selectionKey.channel();
					//將緩衝區清空以備下次讀取
					receivebuffer.clear();
					//讀取伺服器傳送來的資料到緩衝區中
					count=client.read(receivebuffer);
					if(count>0){
						receiveText = new String( receivebuffer.array(),0,count);
						System.out.println("客戶端接受伺服器端資料--:"+receiveText);
						client.register(selector, SelectionKey.OP_WRITE);
					}

				} else if (selectionKey.isWritable()) {//步驟13  非同步寫ByteBuffer到SocketChannel
					sendbuffer.clear();
					client = (SocketChannel) selectionKey.channel();
					sendText = "message from client--" + (flag++);
					sendbuffer.put(sendText.getBytes());
					 //將緩衝區各標誌復位,因為向裡面put了資料標誌被改變要想從中讀取資料發向伺服器,就要復位
					sendbuffer.flip();
					client.write(sendbuffer);
					System.out.println("客戶端向伺服器端傳送資料--:"+sendText);
					client.register(selector, SelectionKey.OP_READ);
				}
			}
			selectionKeys.clear();
		}
	}
}

2、程式碼可用如下圖說明(圖片來自:http://www.open-open.com/lib/view/open1403057331075.html)

       注意:通過debug可以發現Server端handle read後,就直接進行了步驟13,非同步寫操作,這是因為在步驟11進行了write的註冊,因此它不需要client的觸發,這就是selector輪詢的作用。

按照Reactor模式設計和實現,它的服務端通訊序列圖如下:


客戶端通訊序列圖如下:

客戶端步驟4-6的標註有些牽強,其實在大的程式中是這樣的,如hadoop的ipc程式碼中就是這樣,稍後文章會講解hadoop如何使用nio進行rpc通訊。



相關推薦

BIONIO比較例子

java NIO的工作原理:1. 由一個專門的執行緒來處理所有的 IO 事件,並負責分發。 2. 事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。 3. 執行緒通訊:執行緒之間通過 wait,notify 等方式通訊。保證每次上下文切換都是有意義的。減少無謂的執行緒切換。如下圖:一個執行緒Reacto

Java BIONIO比較

前提:相對於傳統IO,NIO將磁碟->核心空間緩衝區->使用者空間緩衝區變為                                              磁碟->使用者空間緩衝區  ,減少了中間的拷貝過程。 BIO讀取過程: 讀取過程

BIO NIO 區別比較

NIO相較於BIO  有個複用路由器。當NIO建立一個新的連結之後,不會直接連線一個執行緒,而是先連線到複用路由器上。該複用路由器可以連線多個執行緒。而BIO是建立一個新的連線之後,該連線直接連到執行緒之上。這樣BIO客戶端請求越大,伺服器就需要更多的執行緒,伺服器會因此而癱

BIONIO、AIO的區別(轉)

特定 應用 api 請求 對數 bind 成才 io模式 事件分發 IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。 一、BIO 在JDK1.4出來之前,我們建立網絡連接的時候采用BIO模式,需要先在服務端啟動一個ServerS

【圖靈學院15】極致優化-高性能網絡編程之BIONIO區別

ice redis let bject 模型 vao 傳輸 示例 ava 一、Java IO概念 1. 一個http請求節點   數據傳輸 1)網絡傳輸   TCP、UDP 2)通信模型   BIO、NIO、AIO   數據處理 3)應用協議   HTTP、RMI、WEB

BIONIO、AIO的區別

繼續 主動 基於 網絡連接 創建 局限 出現 nio mar IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。 一、BIO 在 JDK1.4出來之前,我們建立網絡連接的時候采用BIO模式,需要先在服務端啟動一個ServerSock

IO方式的認知“BIONIO、AIO的區別”

同步和異步 返回 對數 阻塞io 簡單 borde ng- 一定的 兩種 轉自:http://blog.csdn.net/skiof007/article/details/52873421 ------------------------------------------

BIONIO、AIO的區別 【轉載】

不堪 ext 開啟 單獨 選擇 調用 visible 解決 react IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。 一、BIO 在JDK1.4出來之前,我們建立網絡連接的時候采用BIO模式,需要先在服務端啟動一個Server

多執行緒之Locksynchronized比較使用

   第一:先比較兩者的區別: 類別 synchronized

通訊中的BIONIO

從BIO到NIO BIO阻塞式的IO,NIO非阻塞式的IO。這裡從一個通訊的併發問題講起。 我們知道當併發量大的時候我們能夠採用的解決或者是擴充套件方式有兩種:橫向擴充套件(增大執行緒的數目),縱向擴充套件(使得每個執行緒得到良好的使用)。   在併發量大的時候我們可以

BIONIO區別

IO 網路IO、本地IO 網路間的資料傳輸,稱為網路IO 本地磁碟間的資料傳輸,稱為本地IO BIO Blocking IO 阻塞IO 實現原理 伺服器,當每一個請求進來 要接收傳來的資料,都會開啟一個Socket 開啟一個執行緒,來

Java IO:BIONIO區別各自應用場景

引言BIO和NIO是兩種不同的網路通訊模型,現如今NIO已經大量應用在Jetty、ZooKeeper、Netty等開源框架中。一個面向流、一個面向緩衝區一個是阻塞式的、一個非阻塞一個沒有io多路複用器、一個有下面通過一個例子解釋兩者區別:假設當前服務端程式需要同時從與多個客戶

java之BIONIO圖解

    java中網路通訊是通過Socket實現的,Socket分為ServerSocket與Socket兩類;ServerSocket用於服務端,可以通過accept監聽請求,監聽到請求後返回Socket,用於具體完成資料傳輸,而客戶端直接使用Socket發起請求並傳輸資料

ab測試tomcat併發效能(測試BIONIO差別)(一)

二、BIO、NIO、AIO NIO通常採用Reactor模式,AIO通常採用Proactor模式。AIO簡化了程式的編寫,stream的讀取和寫入都有OS來完成,不需要像NIO那樣子遍歷Selector。Windows基於IOCP實現AIO,Linux只有eppoll模擬實現了AIO。 Java7之前

JAVA BIONIO、AIO的區別(這個容易理解)

  IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、非同步非阻塞的AIO。 一、BIO 在JDK1.4出來之前,我們建立網路連線的時候採用BIO模式,需要先在服務端啟動一個ServerSocket,然後在客戶端啟動Socket來對服務端進行通訊,預設情況

BIONIO的方式實現檔案拷貝

面試題 - 程式設計實現檔案拷貝。(這個題目在筆試的時候經常出現,下面的程式碼給出了兩種實現方案) import java.io.FileInputStream; import java.io.F

JAVA BIONIO、AIO的區別

IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、非同步非阻塞的AIO。 一、BIO      在JDK1.4出來之前,我們建立網路連線的時候採用BIO模式,需要先在服務端啟動一個ServerSocket,然後在客戶端啟動Socket來對服務端進行通訊

BIONIO、AIO的區別(這個容易理解)

http://blog.csdn.net/skiof007/article/details/52873421  IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、非同步非阻塞的AIO。 一、BIO 在JDK1.4出來之前,我們建立網路連線的時候採用BIO

為什麼使用solr----solrLucene比較solr 的結構分析

原文連結:http://www.aboutyun.com/thread-7018-1-1.html本帖最後由 nettman 於 2014-2-28 22:46 編輯 可以帶著下面問題來閱讀: 1.搜尋為什麼使用solr? 2.一個索引越來越大,solr是如何應對的? 3.Solr是什麼,一句話描述? 4

BIONIO、AIO的區別(一)

以下內容都是從網上總結而來 java中的IO主要源自於網路和本地檔案   IO的方式通常分為幾種,同步阻塞的BIO、同步非阻塞的NIO、非同步非阻塞的AIO      在JDK1.4出來之前,我們建立網路連線的時候採用BIO模式,需要先在服務端啟動一個Server