nio、bio區別,應運場景

bio阻塞i/o

a.面向流的,InputStream(),OuputStream字節輸入流,字節輸出流,Reader,Writer字符輸入流,字符輸出流
b.阻塞的IO,好比Socket,它的底層用的BIO機制,accept()、connect()、write()調用時會產生阻塞。阻塞模型的侷限性:不可能應對高併發、搞訪問量的場景html

總結:BIO方式適用於鏈接數目比較小而且一次發送大量數據的場景,這種方式對服務器資源要求比較高,併發侷限於應用中java

很簡單轉換成圖型說明就是web瀏覽器發一個請求過來,web服務器就要new 一個線程來處理這個請求,這是傳統的請求處理模型,這也就引來一個很大的問題,當請求越多,服務器端的啓用線程也要越多,咱們都知道linux(window)的文件句柄數有是限的,默認是1024,固然能夠修改,上限好像是65536 ,(一個柄也至關於一個socket也至關於一個thread,linux查看文件句柄Unlimit -a) 其實在實際當中只要併發到1000上下響應請求就會很慢了,因此這種模型是有問題的,這種也就是同步阻塞IO編程(JAVA BIO)
linux

如下是簡單的bio例子:web

Server端代碼:編程

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 
 * @author 
 * @date   2019-02-27
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket();
        socket.bind(new InetSocketAddress(9999));
        while(true) {
            Socket sc = socket.accept();
            new Thread(new Accept(sc)).start();
        }
    }
}
class Accept implements Runnable{
    private Socket sc = null;
    public Accept(Socket sc) {
        sc = this.sc;
    }
    @Override
    public void run() {
        System.out.println("提供服務的線程id是:"+Thread.currentThread().getId());
    }
    
}

Clinet端代碼數組

/**
 * 
 * @author 
 * @date   2019-02-27
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Socket sc = new Socket();
        sc.connect(new InetSocketAddress( "127.0.0.1",9999),0);
        while(true) {
            
        }
    }
}

啓動server端,而後屢次啓動client端,打印結果以下
瀏覽器

nio非阻塞i/o

a.面向緩衝區的(Buffer),NIO是經過緩衝區去操做數據的,能夠用緩衝區靈活操做數據,此外,NIO是面向通道的(Channel),在通道上,便可以讀數據,也能夠寫數據服務器

b.NIO是非阻塞的IO,能夠利用NIO處理高併發和高訪問量的場景,NIO適合處理鏈接數目特別多,可是鏈接比較短(輕操做)的場景,Jetty,Mina,ZooKeeper等都是基於java的nio實現。服務器須要支持超大量的長時間鏈接。好比10000個鏈接以上,而且每一個客戶端並不會頻繁地發送太多數據網絡

首先,引入nio的3個相關概念:
1> Buffer 緩衝區
難用的buffer是一個抽象的對象,下面還有ByteBuffer,IntBuffer,LongBuffer等子類,相比老的IO將數據直接讀/寫到Stream對象,NIO是將全部數據都用到緩衝區處理,它本質上是一個數組,提供了位置,容量,上限等操做方法,仍是直接看代碼代碼來得直接
2>Channel 通道
如自來水管同樣,支持網絡數據從Channel中讀寫,通道寫流最大不一樣是通道是雙向的,而流是一個方向上移動(InputStream/OutputStream),通道可用於讀/寫或讀寫同時進行,它還能夠和下面要講的selector結合起來,有多種狀態位,方便selector去識別. 通道分兩類,一:網絡讀寫(selectableChannel),另外一類是文件操做(FileChannel),咱們經常使用的是上面例子中的網絡讀寫!併發

3>Selector 多路複用選擇器
它是神同樣存在的東西,多路複用選擇器提供選擇已經就緒的任務的能力,也就是selector會不斷輪詢註冊在其上的通道(Channel),若是某個通道發生了讀寫操做,這個通道處於就緒狀態,會被selector輪詢出來,而後經過selectionKey能夠取得就緒的Channel集合,從而進行後續的IO操做.
一個多路複用器(Selector)能夠負責成千上萬個Channel,沒有上限,這也是JDK使用epoll代替了傳統的selector實現,得到鏈接句柄沒有限制.這也意味着咱們只要一個線程負責selector的輪詢,就能夠接入成千上萬個客戶端,這是JDK,NIO庫的巨大進步.

如下是server端代碼:

import java.net.InetSocketAddress;
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 {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(6666));
        
        Selector selector = Selector.open();
        ssc.register(selector,SelectionKey.OP_ACCEPT);
        while(true) {
            selector.select();
            Set<SelectionKey> set = selector.selectedKeys();
            //獲取事件結合的迭代器
            Iterator<SelectionKey> iter = set.iterator();
            while(iter.hasNext()) {
                SelectionKey sk = iter.next();
                if(sk.isAcceptable()) {
                    //證實有客戶端請求創建回遷鏈接
                    ServerSocketChannel ss = (ServerSocketChannel)sk.channel();
                    //建立會話對象
                    SocketChannel sc = null;
                    while(sc==null) {
                        sc = ss.accept();
                    }
                    //設置爲非阻塞模式
                    sc.configureBlocking(false);
                    //輸出爲客戶端提供服務的線程ID
                    System.out.println("有客戶端連入,負責提供服務的"
                            + "線程id:"+Thread.currentThread().getId());
                    //爲sc註冊read事件和write事件
                    sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                }
                if(sk.isReadable()) {
                    //獲取SocketChannel對象
                    SocketChannel sc = (SocketChannel)sk.channel();
                    ByteBuffer buf = ByteBuffer.allocate(10);
                    sc.read(buf);
                    System.out.println("服務器端獨到的內容:"
                            +new String(buf.array()));
                    System.out.println("負責處理讀請求的線程ID:"
                            +Thread.currentThread().getId());
                    sc.register(selector,sk.interestOps()&~SelectionKey.OP_READ);
                }
                if(sk.isWritable()) {
                    //獲取SocketChannel對象
                    SocketChannel sc = (SocketChannel)sk.channel();
                    ByteBuffer buf = ByteBuffer.wrap("over".getBytes());
                    sc.write(buf);
                    System.out.println("負責處理寫請求的線程ID:"
                            +Thread.currentThread().getId());
                    sc.register(selector,sk.interestOps()&~SelectionKey.OP_WRITE);
                }
                //刪除該事件,目的:爲了防止同一個事件被處理屢次
                iter.remove();
            }
            
        }
    }
}

如下是client端代碼

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NIOClient {
    public static void main(String[] args) throws Exception {
        SocketChannel sc = SocketChannel.open();
        sc.configureBlocking(false);
        sc.connect(new InetSocketAddress("127.0.0.1", 6666));
        while(!sc.isConnected()) {
            sc.finishConnect();
        }
        ByteBuffer buf = ByteBuffer.wrap("helloworld".getBytes());
        sc.write(buf);
        ByteBuffer readBuf = ByteBuffer.allocate(9);
        Thread.sleep(1000);
        sc.read(readBuf);
        System.out.println("客戶端讀取到服務器發送過來的內容:"
                +new String(readBuf.array()));
        while(true) {
            
        }
    }
}

這裏補充一點aio,這個比NIO先進的技術,最終實現了
netty
這是神同樣存在的java nio框架, 這個偏底層的東西,可能你接觸較少卻又無處不在,好比:

在業界有一篇沒法超越的netty入門文章,我也沒這個能力超越,只能雙手奉上,大家好好研讀,必然學有所成!
http://ifeve.com/netty5-user-guide/

本文部分參考於:
http://www.javashuo.com/article/p-nrsvskic-ga.html

相關文章
相關標籤/搜索