• Java--NIO(一)


    Java传统的IO是阻塞式的,而NIO则提供了非阻塞式的IO.

    NIO即New IO, java从jdk1.4开始引入,用于解决阻塞式的处理,所以也可以说是No block IO.

    NIO主要是用来处理Socket中阻塞的问题。

    1、NIO组件

    Java NIO 由以下几个核心部分组成:

    • Channels
    • Buffers
    • Selectors

    Channel

    所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。

    Channel和Buffer有好几种类型。下面是JAVA NIO中的一些主要Channel的实现:

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel

    正如你所看到的,这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。

    Buffer

    以下是Java NIO里关键的Buffer实现:

    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

    这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。

    selector

    Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

    这是在一个单线程中使用一个Selector处理3个Channel的图示:

    img

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

    2、Channel、Buffer

    Channel和Buffer在一起使用,channel连接一个目的地,从Buffer中进行读写。

    1、FileChannel、ByteBuffer

    Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。

    FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。

    Buffer可以创建对应大小的缓存区。

    打开FileChannel、创建ByteBuffer

    通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例.

    RandomAccessFile file=new RandomAccessFile("./a.txt","rw");
            
    FileChannel fChannel= file.getChannel();
    

    Buffer没有公开的构造方法,使用allocate方法创建实例,传入一个数字作为容量。

    ByteBuffer buffer=ByteBuffer.allocate(48);
    

    Buffer中关于数据的读写起始位置,可以看做指针:

    Buffer有三个属性:

    • capacity:容量,即创建时指定的数值
    • position:定位,即当前缓冲中指向的位置
    • limit:极限:即当前Buffer中读写所处的极限位置。
    操作Buffer和Channel

    Channel和Buffer之间进行数据读写,和普通IO基本一样,通过write和read方法。

    在对Buffer进行读写,可以使用get,put方法。

    Buffer进行读写之前需要对Buffer中的三个属性进行调整,以便数据读写正确。

    向Buffer中写数据,需要使用clear()与compact()方法,如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。

    如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。

    从Buffer中读数据,需要使用flip方法。

    关闭Channel

    Buffer不需要关闭,jvm会自动处理。

    Channel关闭使用close方法即可。

    从文件中读写数据
    RandomAccessFile file=new RandomAccessFile("./a.txt","rw");
            
            FileChannel fChannel= file.getChannel();//获取通道
    
            ByteBuffer buffer=ByteBuffer.allocate(48);//get Buffer
    
            int len=fChannel.read(buffer);//返回长度
    
            buffer.flip();//进入读模式
    
            byte[] b=new byte[64];
    
            buffer.get(b,0,len);//读到b中len个字节
    
            System.out.println(new String(b,0,len));//输出
    
            buffer.clear();//全部读完,进入写模式
    
            String data="
    now is: "+System.currentTimeMillis();
            buffer.put(data.getBytes());//向通道中写入
            
            buffer.flip();//进入读模式
            
            fChannel.write(buffer);//从Buffer中读出
    
            file.close();
    

    2、SocketChannel、ServerSocketChannel

    Socket才是NIO真正使用的地方。

    ServerSocketChannel可以设置为非阻塞式,这种情况下的网络Io即不会阻塞。

    configureBlocking(false);
    
    打开/关闭Channel

    Socket通过open打开连接,需要一个Socket地址。关闭使用close,非阻塞情况下连接可以自动关闭,无需显示调用。

    SocketChannel sChannel=SocketChannel.open();
     sChannel.connect(new InetSocketAddress("127.0.0.1", 8001));
    
    
    操作

    操作基本一样,如下:

    使用客户端向服务端发送数据,服务端返回数据。

    客户端
    SocketChannel sChannel=SocketChannel.open();
    
            sChannel.connect(new InetSocketAddress("127.0.0.1", 8001));
    
            ByteBuffer buffer=ByteBuffer.allocate(1024);
    
            buffer.put("hello world".getBytes());
            buffer.flip();
    
            sChannel.write(buffer);
    
    
            buffer.clear();
            int len=sChannel.read(buffer);
    
            buffer.flip();
            byte[] b=new byte[1024];
            buffer.get(b, 0, len);
    
            System.out.println(new String(b,0,len));
    
    服务端
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",8001));
    
            serverSocketChannel.configureBlocking(false);
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            while(true){
    			
                //这里不再阻塞,直接向下执行
                SocketChannel socketChannel =
                        serverSocketChannel.accept();
    
                if(socketChannel!=null){
                    int len=socketChannel.read(buffer);
    
                    buffer.flip();
    
                    byte[] b=new byte[1024];
    
                    buffer.get(b,0,len);
                    System.out.println(new String(b,0,len));
    
                    buffer.clear();
    
                    buffer.put("yes".getBytes());
    
                    buffer.flip();
                    socketChannel.write(buffer);
    
                    break;
    
                }
    

    3、Scatter|Gather

    分散与聚集,通道可以与多个Buffer建立关联。

    写入多个Buffer时,如果某个Buffer满了,则转入下个Buffer。

    同样,读取时,某个Buffer被读完,则从其他Buffer读。

    file="b.txt"

    abcdefghijkhello world
    
     RandomAccessFile rFile=new RandomAccessFile(file, "rw");
    
            ByteBuffer buffer1=ByteBuffer.allocate(10);
            ByteBuffer buffer2=ByteBuffer.allocate(10);
            ByteBuffer buffer3=ByteBuffer.allocate(10);
    
            ByteBuffer[] buffers={buffer1,buffer2,buffer3};
    
            FileChannel fc= rFile.getChannel();
    
            long l=fc.read(buffers);//读到buffers中,buffer1满则读到buffer2
            System.out.println("buffers "+l);
            buffer3.flip();
            buffer2.flip();
            buffer1.flip();
            System.out.println((char)buffer1.get());
            System.out.println((char)buffer2.get());
            System.out.println((char)buffer3.get());
    
    
            buffer1.clear();
            buffer2.clear();
            buffer3.clear();
          
            buffer1.put("scatter".getBytes());
            buffer2.put("gatter".getBytes());
    
            buffer3.flip();
            buffer2.flip();
            buffer1.flip();
           
            fc.write(buffers);//从buffers中写出,buffer1写完,则从buffer2中写。
           
    
  • 相关阅读:
    python基础——操作系统简介
    python 3 输入和输出
    C++学习笔记——C++简介
    ML机器学习导论学习笔记
    Linux常用基本指令——文件处理命令
    Oracle数据库从入门到精通-分组统计查询
    PL/SQL编程基础——PL/SQL简介
    django 第五课自定义模板过滤器与标签
    Python之GUI的最终选择(Tkinter)
    Python利用pandas处理Excel数据的应用
  • 原文地址:https://www.cnblogs.com/cgl-dong/p/13856179.html
Copyright © 2020-2023  润新知