(2) study and application of NIO

The concept of blocking and non-blocking

  • The traditional IO are blocking the flow . That is, when a thread calls read () or write (), the thread is blocked until there is some data to be read or written to, the thread in the meantime can not perform other tasks. Therefore, upon completion of the communication network IO operation, because the thread is blocked, so the server must have a separate thread for processing for each client, when the server needs to handle large number of clients, a sharp decline in performance.

  • Java NIO is non-blocking mode . When the thread to read and write data from a channel, if no data is available, the thread can perform other tasks. The threads are usually non-blocking IO idle time for performing IO operations on other channels, so a separate thread to manage a plurality of input and output channels. Therefore, NIO allows the server to use one or a limited number of threads to simultaneously handle all client connections to the server.

Clog

  • NIO using network communication to three core

       ①, channel (Channel) : responsible for connecting

               java.nio.channels.Channel接口
                  SelectableChannel
                  SocketChannel
                  ServerSocketChannel
                  DatagramChannel

                  Pipe.SinkChannel
                  Pipe.SourceChannel

       ②, buffer (Buffer) : responsible for data access

       ③, a selector (Selector) : is SelectableChannel multiplexer. IO for condition monitoring of SelectableChannel

  • Selector (Selector) application:

       When you call register (Selector sel, int ops) will register the channel selector, the selector listen to events on the channel, you need to specify the second parameter ops.

       Can listen to the type of event (with four constants can be used SelectionKey representation):
          Reading : SelectionKey.OP_READ

           : SelectionKey.OP_WRITE

          Connection : SelectionKey.OP_CONNECT

           Reception : SelectionKey.OP_ACCEPT

       More than one event listener, you can use "bit or" operator if registered.
example:
Here Insert Picture Description

Blocking code is as follows :

//客户端
@Test
public void client() throws Exception{
    //1,获取通道
    SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
    FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"),StandardOpenOption.READ);
    //2,分配指定大小的缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    //3,读取本地文件,并发送到服务端
    while (inChannel.read(byteBuffer) != -1){
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        byteBuffer.clear();
    }
    socketChannel.shutdownOutput();
    //接收服务端的反馈
    int len = 0;
    while ((len = socketChannel.read(byteBuffer)) != -1){
        byteBuffer.flip();
        System.out.println(new String(byteBuffer.array(),0,len));
        byteBuffer.clear();
    }
    //4,关闭通道
    inChannel.close();
    socketChannel.close();
}
//服务端
@Test
public void server() throws Exception{
    //1,获取通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    //2,绑定连接
    serverSocketChannel.bind(new InetSocketAddress(9898));
    //3,获取客户端连接的通道
    SocketChannel accept = serverSocketChannel.accept();
    //4,分配指定大小的缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    //5,接收客户端的数据,并保存到本地
    while (accept.read(byteBuffer) != -1){
        byteBuffer.flip();
        outChannel.write(byteBuffer);
        byteBuffer.clear();
    }
    //发送反映给客户端
    byteBuffer.put("图片已收到,Over! Over!".getBytes());
    byteBuffer.flip();
    accept.write(byteBuffer);

    //6,关闭通道
    outChannel.close();
    accept.close();
    serverSocketChannel.close();
}

Non-blocking

code show as below:

//客户端
@Test
public void client() throws Exception {
    //1,获取通道
    SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888));
    //2,切换非阻塞式模式
    socketChannel.configureBlocking(false);
    //3,范围内配指定大小的缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    //4,发送数据给服务端
    int num = 5;
    while (num > 0) {
        byteBuffer.put((LocalDateTime.now().toString() + ":" + num).getBytes());
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        byteBuffer.clear();
        --num;
    }
    //5,关闭通道
    socketChannel.close();
}

//服务端
@Test
public void server() throws Exception {
    //1,获取通道
    ServerSocketChannel ssChannel = ServerSocketChannel.open();
    //2,切换非阻塞模式
    ssChannel.configureBlocking(false);
    //3,绑定连接
    ssChannel.bind(new InetSocketAddress(8888));
    //4,获取选择器
    Selector selector = Selector.open();
    //5,将通道注册到选择器上,并指定“监听接收事件”
    ssChannel.register(selector, SelectionKey.OP_ACCEPT);
    //6,轮询式的获取选择器上已经“准备就绪”的事件
    while (selector.select() > 0) {
        //7,获取当前选择器中所有注册的“选择器(已就绪的监听事件)”
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            //8,获取准备“就绪”的事件
            SelectionKey sk = iterator.next();
            //9,判断具体是什么事件准备就绪
            if (sk.isAcceptable()) {
                //10,若“接收就绪”,获取客户端连接
                SocketChannel sChannel = ssChannel.accept();
                //11,切换非阻塞模式
                sChannel.configureBlocking(false);
                //12,将该通道注册到选择器上
                sChannel.register(selector, SelectionKey.OP_READ);
            } else if (sk.isReadable()) {
                //13,获取当前选择器上“读就绪”状态的通道
                SocketChannel sChannel = (SocketChannel) sk.channel();
                //14,读取数据
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int len = 0;
                while ((len = sChannel.read(byteBuffer)) > 0) {
                    byteBuffer.flip();
                    System.out.println(new String(byteBuffer.array(),0, len));
                    byteBuffer.clear();
                }
            }
            iterator.remove();
        }
    }
}

DatagramChannel

  • The Java NIO DatagramChannel is a channel can send and receive UDP packets.

  • Steps:

       ①, open DatagramChannel

       ②, receiving / transmitting data. code show as below:

@Test
    public void send() throws Exception{
        DatagramChannel dc = DatagramChannel.open();
        dc.configureBlocking(false);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int i = 5;
        while (i > 0){
            byteBuffer.put(("Tommey周"+i+"号").getBytes());
            byteBuffer.flip();
            dc.send(byteBuffer, new InetSocketAddress("127.0.0.1",9999));
            byteBuffer.clear();
            i--;
        }
        dc.close();
    }
    @Test
    public void receive() throws Exception{
        DatagramChannel dc = DatagramChannel.open();
        dc.configureBlocking(false);
        dc.bind(new InetSocketAddress(9999));
        Selector selector = Selector.open();
        dc.register(selector,SelectionKey.OP_READ);
        while (selector.select() > 0){
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey sk = iterator.next();
                if (sk.isReadable()){
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    dc.receive(byteBuffer);
                    byteBuffer.flip();
                    System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
                    byteBuffer.clear();
                }
            }
            iterator.remove();
        }
    }

Pipe Pipeline

  • Java the NIO pipes are unidirectional data connection between the two threads . Pipe passage has a source and a sink channel. Sink channel data will be written, it is read from the source channels. code show as below:
    Here Insert Picture Description
@Test
public void testPiPe() throws Exception{
    //1,获取管道
    Pipe pipe = Pipe.open();
    //2,将缓冲区中的数据写入管道
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    Pipe.SinkChannel sink = pipe.sink();
    byteBuffer.put("通过单向管道发送数据".getBytes());
    byteBuffer.flip();
    sink.write(byteBuffer);

    //3,读取缓冲区中的数据
    Pipe.SourceChannel source = pipe.source();
    byteBuffer.flip();
    int len = source.read(byteBuffer);
    System.out.println(new String(byteBuffer.array(),0, len));

    source.close();
    sink.close();
}
Published 67 original articles · won praise 19 · views 9859

Guess you like

Origin blog.csdn.net/qq_41530004/article/details/104686547