Netty之NIO

作者: 任嘉平生愿 | 来源:发表于2018-06-01 10:43 被阅读66次

    ------NIO简介(1)--------

    NIO组件

    channel,buffer,selector,pip,filelock。

    Channel分为:

    FileChannel

    DatagramChannel

    SocketChannel

    ServerSocketChannel

    UDP 和 TCP 网络IO,以及文件IO

    Buffer分为:

    ByteBuffer

    CharBuffer

    DoubleBuffer

    FloatBuffer

    IntBuffer

    LongBuffer

    ShortBuffer

    byte, short, int, long, float, double 和 char。

    结构图

                      thread

                        |

                    selector

        |              |            |

    channel          channel      channel

    使用Selector,得向Selector注册Channel,然后调用它的select()方法。

    这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回

    线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等

    --------channel(2)---------

    channel 可以理解为pip

    channel <=======> buffer

    FileChannel 从文件中读写数据。

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

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

    ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。

    对每一个新进来的连接都会创建一个SocketChannel。 

    例子:

                                    NIO

    File  -  channel -  buffer =============== application

    buf.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据

    --------NIO buffer(3)------------

    四个步骤:

    写入数据到Buffer

    调用flip()方法

    从Buffer中读取数据

    调用clear()方法或者compact()方法

    flip()方法

    反转IO  写=========》读

    capacity

    position

    capacity /limit      -

    要想获得一个Buffer对象首先要进行分配。

    allocate方法。

    ByteBuffer buf = ByteBuffer.allocate(48);

    --------向Buffer中写数据-----

    从Channel写到Buffer。

    通过Buffer的put()方法写到Buffer里。

    int bytesRead = inChannel.read(buf); //read into buffer.

    buf.put(127);

    调用flip()方法会将position设回0,并将limit设置成之前position的值。

    ----从Buffer中读取数据----

    从Buffer读取数据到Channel。

    使用get()方法从Buffer中读取数据。

    int bytesWritten = inChannel.write(buf);

    byte aByte = buf.get();

    rewind()方法

    Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变

    如果调用的是clear()方法.

    position将被设回0,limit被设置成 capacity的值,Buffer 被清空了

    compact()方法

    如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,

    compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面

    mark()与reset()方法

    通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。

    之后可以通过调用Buffer.reset()方法恢复到这个position。

    equals()方法

    有相同的类型(byte、char、int等)。

    Buffer中剩余的byte、char等的个数相等。

    Buffer中所有剩余的byte、char等都相同。

    compareTo()方法

    ompareTo()方法比较两个Buffer的剩余元素

    第一个不相等的元素小于另一个Buffer中对应的元素 。

    第一个Buffer的元素个数比另一个少)

    --------------Scatter/Gather(4)-----------------

    scatter/gather用于描述从Channel

    scatter  读操作时将读取的数据写入多个buffer中

    gather  写操作时将多个buffer的数据写入同一个Channel

    scatter

    经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,

    你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

    ByteBuffer header = ByteBuffer.allocate(128);

    ByteBuffer body  = ByteBuffer.allocate(1024);

    ByteBuffer[] bufferArray = { header, body };

    channel.read(bufferArray);

    read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,

    当一个buffer被写满后,channel紧接着向另一个buffer中写。

    这也意味着它不适用于动态消息(译者注:消息大小不固定)

    ----Gathering---------

    Gathering Writes是指数据从多个buffer写入到同一个channel。

    ByteBuffer header = ByteBuffer.allocate(128);

    ByteBuffer body  = ByteBuffer.allocate(1024);

    ByteBuffer[] bufferArray = { header, body };

    channel.write(bufferArray);

    注意只有position和limit之间的数据才会被写入。

    因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据

    ------------(五) 通道之间的数据传输---------------------

    直接将数据从一个channel传输到另外一个channel。

    transferFrom()

    FileChannel  ===========>  FileChannel

    RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");

    FileChannel      fromChannel = fromFile.getChannel();

    RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");

    FileChannel      toChannel = toFile.getChannel();

    long position = 0;

    long count = fromChannel.size();

    toChannel.transferFrom(position, count, fromChannel);

    transferTo()

    FileChannel ===========>  Channel

    -----------------------(六) Selector--------------

    Selector(选择器)是Java NIO中能够检测一到多个NIO通道

    一个单独的线程可以管理多个channel

    Selector的创建 

    Selector selector = Selector.open();

    向Selector注册通道

    SelectableChannel.register()

    channel.configureBlocking(false);

    SelectionKey key = channel.register(selector,

        Selectionkey.OP_READ);

    Connect

    Accept

    Read

    Write

    int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

    SelectionKey

    interest集合

    ready集合

    Channel

    Selector

    附加的对象(可选)

    Channel  channel  = selectionKey.channel();

    Selector selector = selectionKey.selector();

    通过Selector选择通道

    下面是select()方法:

    int select()

    int select(long timeout)

    int selectNow()

    完整的示例

    Selector selector = Selector.open();

    channel.configureBlocking(false);

    SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

    while(true) {

      int readyChannels = selector.select();

      if(readyChannels == 0) continue;

      Set selectedKeys = selector.selectedKeys();

      Iterator keyIterator = selectedKeys.iterator();

      while(keyIterator.hasNext()) {

        SelectionKey key = keyIterator.next();

        if(key.isAcceptable()) {

            // a connection was accepted by a ServerSocketChannel.

        } else if (key.isConnectable()) {

            // a connection was established with a remote server.

        } else if (key.isReadable()) {

            // a channel is ready for reading

        } else if (key.isWritable()) {

            // a channel is ready for writing

        }

        keyIterator.remove();

      }

    }

    -------(七) FileChannel--------------

    连接到文件的通道

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

    打开FileChannel

    InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例

    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");

    FileChannel inChannel = aFile.getChannel();

    从FileChannel读取数据

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf);

    向FileChannel写数据

    String newData = "New String to write to file..." + System.currentTimeMillis();

    ByteBuffer buf = ByteBuffer.allocate(48);

    buf.clear();

    buf.put(newData.getBytes());

    buf.flip();

    while(buf.hasRemaining()) {

        channel.write(buf);

    }

    关闭FileChannel

    channel.close();

    FileChannel的某个特定位置进行数据的读/写操作

    position方法

    long pos = channel.position();

    channel.position(pos +123);

    FileChannel的size方法

    long fileSize = channel.size();

    FileChannel的truncate方法

    截取文件,后面的部分将被删除

    channel.truncate(1024);

    FileChannel的force方法

    FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。

    channel.force(true);

    ---------(八) SocketChannel------------

    连接到TCP网络套接字的通道

    创建SocketChannel

    打开一个SocketChannel并连接到互联网上的某台服务器。

    一个新连接到达ServerSocketChannel时,会创建一个SocketChannel

    打开 SocketChannel

    SocketChannel socketChannel = SocketChannel.open();

    socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

    关闭 SocketChannel

    socketChannel.close();

    从 SocketChannel 读取数据

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = socketChannel.read(buf);

    分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中

    返回的int值表示读了多少字节进Buffer里

    写入 SocketChannel

    new string - new buf - buf clear - buf put - buf flip - buf remain - channel write

    非阻塞模式

    设置 SocketChannel 为非阻塞模式(non-blocking mode)

    异步模式下调用connect(), read() 和write()了。

    connect()

    socketChannel.configureBlocking(false);

    socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

    while(! socketChannel.finishConnect() ){

        //wait, or do something else...

    }

    非阻塞模式与选择器

    非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,

    可以询问选择器哪个通道已经准备好了读取,写入等

    --------(九) ServerSocketChannel---------

    ServerSocketChannel 是一个可以监听新进来的TCP连接的通道

    例子:

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    serverSocketChannel.socket().bind(new InetSocketAddress(9999));

    while(true){

        SocketChannel socketChannel =

                serverSocketChannel.accept();

        //do something with socketChannel...

    }

    打开 ServerSocketChannel

    关闭 ServerSocketChannel

    监听新进来的连接

    ServerSocketChannel.accept() 方法监听新进来的连接

    返回一个包含新进来的连接的 SocketChannel

    非阻塞模式

    非阻塞模式下,accept() 方法会立刻返回

    如果还没有新进来的连接,返回的将是null。

    需要检查返回的SocketChannel是否是null

    if(socketChannel != null){

            //do something with socketChannel...

        }

    --------(十) Java NIO DatagramChannel-------

    DatagramChannel是一个能收发UDP包的通道

    UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。

    打开 DatagramChannel

    DatagramChannel channel = DatagramChannel.open();

    channel.socket().bind(new InetSocketAddress(9999));

    接收数据

    receive()

    ByteBuffer buf = ByteBuffer.allocate(48);

    buf.clear();

    channel.receive(buf);

    发送数据

    send()方法

    String newData = "New String to write to file..." + System.currentTimeMillis();

    ByteBuffer buf = ByteBuffer.allocate(48);

    buf.clear();

    buf.put(newData.getBytes());

    buf.flip();

    int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));

    连接到特定的地址

    channel.connect(new InetSocketAddress("jenkov.com", 80));

    当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。

    只是在数据传送方面没有任何保证。这里有几个例子:

    ----------(十一) Pipe--------------

    管道是2个线程之间的单向数据连接

    Pipe有一个source通道和一个sink通道

    数据会被写到sink通道,从source通道读取。

    ThreadA - sink - source - ThreadB

    创建管道

    Pipe pipe = Pipe.open();

    向管道写数据

    Pipe.SinkChannel sinkChannel = pipe.sink();

    String newData = "New String to write to file..." + System.currentTimeMillis();

    ByteBuffer buf = ByteBuffer.allocate(48);

    buf.clear();

    buf.put(newData.getBytes());

    buf.flip();

    while(buf.hasRemaining()) {

        sinkChannel.write(buf);

    }

    从管道读取数据

    Pipe.SourceChannel sourceChannel = pipe.source();

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = sourceChannel.read(buf);

    ------------(十二) Java NIO与IO-----------

    IO                NIO

    面向流            面向缓冲

    阻塞IO            非阻塞IO

    无                选择器

    面向流与面向缓冲

    相关文章

      网友评论

        本文标题:Netty之NIO

        本文链接:https://www.haomeiwen.com/subject/qvyhsftx.html