java nio

1、什麼是java nio前端

  java nio 是java new i/o的簡稱,也有叫java non-blocking i/o,在jdk1.4中引入。它是一種同步非阻塞的io模型,也是io多路複用的基礎。java

2、nio技術組成緩存

  java NIO主要由三部分組成①channels、②selecters、③Buffers 服務器

  一、Channel(通道)相似於bio中的stream,數據能夠從channel讀取到buffer中,也能夠從buffer寫入channel中。channel和stream的不一樣點是stream是單向的,二channel是雙向的。下面是一些常見的通道,網絡

    FileChannel  從文件中讀寫數據,不能設置爲非阻塞模式,也就不能與selector在一塊兒使用app

    DatagramChannel 經過UDP讀寫網絡中的數據dom

    SocketChannel  經過TCP讀寫網絡中的數據socket

    ServerSocketChannel  能夠監聽新進來的TCP連接,對每個新進來的TCP連接都會建立一個SocketChannelpost

 

  2.Buffers 緩存,用來緩存從channel中讀取的數據,也能夠將數據從緩存寫入channel中。spa

    ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer  從命名可知分別對應了幾種基本數據類型。

    MappedByteBuffer 是ByteBuffer的子類,用於表示內存映射文件緩存,可用於複製大型文件。

  Buffer的屬性和一些經常使用的方法:

    屬性:mark <= position <= limit <= capacity

      capacity :緩存區的總容量

      position :緩衝區中,下一次發生讀取或寫入操做的地方,每次讀寫操做後,都會向後推動。

      limit:在讀模式下,limit和positon一相同;在寫模式下,limit設置爲上次讀取時position的地方

      mark:標記值,當調用了mark()方法後,mark就是當前的position位置;調用reset()以後,會將postion置爲mark的位置。

      

    方法:

         allocate(n)   初始化緩衝區的大小,此時position=limit=0 ,capacity  = n, mark= -1

      put()  向緩衝區寫數據

      get() 向緩衝區讀數據  

      filp() 將緩衝區從寫模式切換到讀模式   

      clear()  從讀模式切換到寫模式,不會清空數據,但後續寫數據會覆蓋原來的數據,即便有部分數據沒有讀,也會被遺忘;將position和limit都置爲0,將mark置爲-1

       compact()  從讀數據切換到寫模式,數據不會被清空,會將全部未讀的數據copy到緩衝區頭部,後續寫數據不會覆蓋,而是在這些數據以後寫數據

      mark() 對position作出標記,配合reset使用

      reset()  將position置爲標記值

      rewind() 將position置爲0,mark置爲-1

 

  3.selector 選擇器,用於監聽在selector上註冊了的channel的事件,只能用於非阻塞channel。經過調用它的select()或其重載方法,這個方法會一直阻塞,直到註冊的通道中有事件就緒。

    在selector上註冊channel時須要指定監聽的事件,總共有以下四種事件可供選擇:

      SelectionKey.OP_CONNECT   若是某個channel成功鏈接到另外一個服務器稱爲「鏈接就緒」

      SelectionKey.OP_ACCEPT   若是一個ServerSocketChannel準備好接收新進入的鏈接稱爲「接收就緒」

      SelectionKey.OP_READ      若是一個有數據可讀的通道能夠說是「讀就緒」

      SelectionKey.OP_WRITE 等待寫數據的通道能夠說是「寫就緒」

 

  下面是一些nio方法操做文件的例子:

  

//NIO讀寫文件
public class Test3 {

    public static void main(String[] args) {
        String path = "E:/a.txt";
        readFile(path);
        String path2 = "E:/b.txt";
        writeFile(path2);
    }
    
    /**
     * 
    * @Title: readFile
    * @Description:使用FileChannel讀取文本文件
    * @param @param path
    * @return void
    * @throws 
     */
    public static void readFile(String path){
        FileInputStream fis = null;
        FileChannel fc = null;
        try {
            //Java.nio.charset.Charset處理了字符轉換問題。它經過構造CharsetEncoder和CharsetDecoder將字符序列轉換成字節和逆轉換。
            Charset charset = Charset.forName("utf-8");  
            CharsetDecoder decoder = charset.newDecoder();
            
            fis = new FileInputStream(path);
            fc = fis.getChannel();
            
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 
            CharBuffer charBuffer = CharBuffer.allocate(1024);
            while (fc.read(byteBuffer) != -1) { 
                byteBuffer.flip();
                decoder.decode(byteBuffer, charBuffer, false);//若是不夠一個字符不會進行解碼
                charBuffer.flip();
                while (charBuffer.hasRemaining()) {
                    System.out.print(charBuffer.get());
                } 
                byteBuffer.compact();//將緩衝區設置爲讀模式,而且上一次沒有轉換的字節會被保留下來,並放在最前端,不能用clear()代替
                charBuffer.compact();
            }
            fis.close();
            fc.close();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                fis.close();
                fc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    /**
     * 
    * @Title: writeFile
    * @Description:使用FileChannel將文字寫入文件中
    * @param @param path
    * @return void
    * @throws 
     */
    public static void writeFile(String path){
        
        FileOutputStream fos = null;
        FileChannel fileChannel = null;
        try {
            String source = "我要寫入一個文件中使得發放卡號便可adda";
            fos = new FileOutputStream(path);
            fileChannel = fos.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put(source.getBytes("utf-8"));
            buffer.flip();
            fileChannel.write(buffer);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                fos.close();
                fileChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
//nio複製文件
public class Test4 {

    public static void main(String[] args) throws IOException {
        String source = "E:/BaiduNetdisk_5.6.3.exe";
        String target = "E:/BaiduNetdisk_5.6.3_copy.exe";
//        bioCopyFile(source,target);
//        nioCopyFile(source,target);
        mappedCopyFile(source,target);
    }
    
    /**
     * 
    * @Title: bioCopyFile
    * @Description: java 標準io流複製文件
    * @param @throws IOException
    * @return void
    * @throws 
     */
    public static void bioCopyFile(String source,String target)throws IOException {
        long startTime = System.currentTimeMillis();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target));
        int len = 0;
        while((len = bis.read()) != -1){
            bos.write(len);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("bio複製文件所用時間"+(endTime-startTime)+"毫秒");
        bis.close();
        bos.close();
    }
    
    /**
     * 
    * @Title: nioCopyFile
    * @Description: java nio複製文件
    * @param @throws IOException
    * @return void
    * @throws 
     */
    public static void nioCopyFile(String source,String target)throws IOException {
        long startTime = System.currentTimeMillis();
        RandomAccessFile raf = new RandomAccessFile(source,"rw");
        RandomAccessFile raf2 = new RandomAccessFile(target,"rw");
        FileChannel inChannel = raf.getChannel();
        FileChannel outChannel = raf2.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(inChannel.read(buffer) != -1){
            buffer.flip();//寫模式切換到讀模式
            outChannel.write(buffer);
            buffer.clear();//讀模式切換到寫模式
        }
        raf.close();
        raf2.close();
        inChannel.close();
        outChannel.close();
        long endTime = System.currentTimeMillis();
        System.out.println("nio複製文件所用時間"+(endTime-startTime)+"毫秒");
    }
    
    /**
     * 
    * @Title: mappedCopyFile
    * @Description: 使用MappedByteBuffer來複制文件
    * MappedByteBuffer的確快,但也存在一些問題,主要就是內存佔用和文件關閉等不肯定問題。
    * 被MappedByteBuffer打開的文件只有在垃圾收集時纔會被關閉,而這個點是不肯定的
     */
    public static void mappedCopyFile(String source,String target) throws IOException{
        long startTime = System.currentTimeMillis();
        RandomAccessFile raf = new RandomAccessFile(source,"rw");
        RandomAccessFile raf2 = new RandomAccessFile(target,"rw");
        FileChannel inChannel = raf.getChannel();
        FileChannel outChannel = raf2.getChannel();
        MappedByteBuffer mappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        outChannel.write(mappedByteBuffer);
        raf.close();
        raf2.close();
        inChannel.close();
        outChannel.close();
        long endTime = System.currentTimeMillis();
        System.out.println("MappedByteBuffer複製文件所用時間"+(endTime-startTime)+"毫秒");
    }

}
//nio服務器
public class Server {
    private Selector selector;//同一個選擇器,用來監聽是否有連接或者其餘操做就緒
    
    
    public static void main(String[] args) {
        Server server = new Server();
        server.startServer();//啓動服務器時監聽端口是否有新的鏈接事件
        server.listenChannel();//監聽全部的channel有沒有數據讀取準備就緒
    }
    
    public void startServer(){
        try {
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);
            ssc.socket().bind(new InetSocketAddress(8888));//監聽8888端口
            selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void listenChannel(){
        while(true){
            try {
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectedKeys.iterator();
                while(iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    handleKey(key);
                    iterator.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private void handleKey( SelectionKey key){
        try {
            if(key.isAcceptable()){//有新的網絡能夠連接
                ServerSocketChannel channel = (ServerSocketChannel)key.channel();
                SocketChannel socketChannel = channel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            }
            if(key.isReadable()){
                SocketChannel channel = (SocketChannel)key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int read = channel.read(buffer);
                if(read > 0){
                    String receiveText = new String(buffer.array(),0,read);
                    System.out.println("服務器端接受到的數據---"+receiveText);
                    channel.register(selector, SelectionKey.OP_WRITE);
                }
            }
            if(key.isWritable()){
                SocketChannel channel = (SocketChannel)key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                buffer.clear();
                buffer.put("服務器響應信息".getBytes());
                buffer.flip();
                channel.write(buffer);
                channel.register(selector, SelectionKey.OP_READ);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//nio客戶端
public class Client {
    
    public static void main(String[] args) {
            clientStart();
    }
    
    public static void clientStart(){
        try {
            Selector selector = Selector.open();
            SocketChannel sc = SocketChannel.open();
            sc.configureBlocking(false);
            sc.connect(new InetSocketAddress("localhost", 8888));
            sc.register(selector, SelectionKey.OP_CONNECT);
            
            
            while(true){
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectedKeys.iterator();
                while(iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    //iterator.remove();
                    if(key.isConnectable()){
                        System.out.println("client connect");  
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        // 判斷此通道上是否正在進行鏈接操做。  
                        // 完成套接字通道的鏈接過程。  
                        ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
                        //完成鏈接的創建(TCP三次握手)
                        if (socketChannel.isConnectionPending()) {
                            socketChannel.finishConnect(); 
                            System.out.println("完成鏈接!");  
                            sendBuffer.put("Hello,Server".getBytes());  
                            sendBuffer.flip();  
                            socketChannel.write(sendBuffer);
                        }
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    
                    if(key.isReadable()){
                        SocketChannel channel = (SocketChannel) key.channel();  
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int read=channel.read(buffer);  
                        if(read>0){  
                            String receiveText = new String( buffer.array(),0,read);  
                            System.out.println("客戶端接受服務器端數據--:"+receiveText);  
                            channel.register(selector, SelectionKey.OP_WRITE);  
                        }  
                    }
                    if(key.isWritable()){
                        SocketChannel socketChannel = (SocketChannel)key.channel();
                        ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
                        sendBuffer.clear();  
                        sendBuffer.put("MSG from client".getBytes());  
                        sendBuffer.flip();  
                        socketChannel.write(sendBuffer);
                        socketChannel.register(selector, SelectionKey.OP_READ); 
                    }
                }
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
 }
相關文章
相關標籤/搜索