zoukankan      html  css  js  c++  java
  • java nio

    一、什么是java nio

      java nio 是java new i/o的简称,也有叫java non-blocking i/o,在jdk1.4中引入。它是一种同步非阻塞的io模型,也是io多路复用的基础。

    二、nio技术组成

      java NIO主要由三部分组成①channels、②selecters、③Buffers 

      1、Channel(通道)类似于bio中的stream,数据可以从channel读取到buffer中,也可以从buffer写入channel中。channel和stream的不同点是stream是单向的,二channel是双向的。下面是一些常见的通道,

        FileChannel  从文件中读写数据,不能设置为非阻塞模式,也就不能与selector在一起使用

        DatagramChannel 通过UDP读写网络中的数据

        SocketChannel  通过TCP读写网络中的数据

        ServerSocketChannel  可以监听新进来的TCP链接,对每一个新进来的TCP链接都会创建一个SocketChannel

      2.Buffers 缓存,用来缓存从channel中读取的数据,也可以将数据从缓存写入channel中。

        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();
            }
            
        }
     }

        

      

  • 相关阅读:
    Java之事件处理
    Java之图形程序设计
    小议设置path环境变量
    关于JAVA中的编译和解释执行
    并发工具类 CountDownLatch
    线程池
    Properties的小问题
    转换流
    TCP中客户端和服务器的理解
    leetcode_160. 相交链表
  • 原文地址:https://www.cnblogs.com/kyleinjava/p/8856180.html
Copyright © 2011-2022 走看看