Java IO 模型: BIO, NIO, AIO

Java IO 模型: BIO, NIO, AIO

Originally intended to direct learning network framework Netty, but the first to add a bit of their own learning and understanding of several Java IO model. Are BIO, NIO, AIO three kinds of IO models.

Basic instructions IO model

image-20200324063448217

BIO model diagram

image-20200324064030007

Disadvantages:

  1. If there are multiple Client, it will produce a lot of threads. The main pressure is on the server side. Pressure client is not significant.

  2. In addition, after a connection is not in use all the time. There will be space-time.

  3. It will be blocked.

NIO model diagram

image-20200324064940368

Features:

  1. Event-driven
  2. Multiplexer
  3. NIO underlying model used Netty

AIO models

It has not yet been widely used. Asynchronous non-blocking. We can first understand.

BIO, NIO, AIO usage scenario analysis

  1. BOI manner using a relatively small number of connections fixed architecture, server resources in this way is relatively high, limited concurrent applications. JDK1.4 previously only choice. But the program is simple and easy to understand.
  2. NIO manner suitable for the number of connections is large and a relatively short connection (light operation) architecture, such as chat server, barrage systems, communications services period. Programming is more complex, JDK1.4 began to support.
  3. AIO mode applies to the number of connections and the connection more than long (heavy operation) architecture, such as the album server, call the full OS operating system to participate in concurrent operation, programming is more complex, JDK7 began to support. But it has not yet been widely used.

JAVA BIO Programming

JAVA BIO basic introduction

image-20200324070601084

JAVA BIO mechanism

image-20200324070450273

JAVA application examples BIO

image-20200324070636449

package com.dawa.netty.bio;

import com.sun.org.apache.xpath.internal.operations.String;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 功能需求
 * 1. 使用BIO模型编写一个服务器,监听6666端口,当有客户连接的时候,就启动一个客户端线程与之连接
 * 2. 要求使用县城连接机制,可以连接过个客户端
 * 3. 服务器端可以接受客户端发送的数据(TeInet方法即可)
 */
public class TestBIO {
    public static void main(String[] args) throws Exception {
        //1. 创建一个线程池. 这里 借助 Executors 这个工具类
        ExecutorService pool = Executors.newCachedThreadPool();
        //2. 建立一个监听服务,用来监听客户端连接
        ServerSocket serverSocket = new ServerSocket(6666);

        while (true) {
            final Socket socket = serverSocket.accept();
            System.out.println("一个客户端连接");
            //就创建一个线程与之通信
            pool.execute(new Runnable() {
                public void run() {
                    //编写一个处理方法.
                    handler(socket);
                }
            });
        }
    }

    public static void handler(Socket socket) {
        byte[] bytes = new byte[1024];

        try (InputStream inputStream = socket.getInputStream()) {
            while (true) {
                int read = inputStream.read(bytes);
                if (read != -1) {
                    //注意这里,不能用String转换了.因为String已经不支持有参数的构造方法.
                    System.out.println(Arrays.toString(bytes));
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭连接");
        }

    }
}

JAVA BIO Analysis

image-20200324073218636

JAVA NIO programming

NIO basic introduction

image-20200324193714767

image-20200324195127910

image-20200324194859453

  1. NIO's three core, channel equivalent of Socket IO
  2. Buffer, Channel, Selector (selector) three core components.
  3. Buffer realized by non-blocking,
  4. For buffer, or block-oriented programming (Buffer is such).
  5. NIO is event-driven

Basic use NIO Buffer

image-20200324200742367

This is not BooleanBuffer, inherited from another StringBuffer StringBuilder.

Use a simple case of Buffer subclasses as follows

package com.dawa.netty.bio;

import java.nio.IntBuffer;

public class TestNIO {
    public static void main(String[] args) {
        IntBuffer intBuffer = IntBuffer.allocate(5);

        for (int i = 0; i < intBuffer.capacity(); i++) {
            System.out.println(intBuffer.put(i*5));
        }

        intBuffer.flip();

        while (intBuffer.hasRemaining()) {
            System.out.println(intBuffer.get());
        }
    }
}

Compare the NIO and BIO

image-20200324201708534

NIO schematic three core principles

image-20200324201933663

Selector, Channel and Buffer relationship diagram

  1. Each Channel will correspond to a Buffer.
  2. Selector corresponds to a thread, a thread corresponding to the plurality Channel (connected).
  3. This figure reflects the three registered Channel Selector
  4. Switches to Channel which is determined by the event. Event is an important concept.
  5. Select based on different events, each channel is switched on.
  6. Buffer is a memory block, the bottom layer is an array
  7. Writing data read by Buffer, and this BIO, BIO either the input stream or output stream, not bidirectional, but the NIO Buffer is written may be read, required Flip () method to switch.
  8. Channel both ways, the situation can return to the underlying operating system, such as Linux, the underlying operating system is a two-way channel.

Detailed three core --Buffer buffer

Buffer the source Doc (java11)

image-20200324204447270

basic introduction

image-20200324204604333

Buffer subclass

image-20200324200742367

Container object (function group), how to understand? We can see from the source code. Int, Float etc. Each subclass Buffer objects are [] array.

image-20200324204830461

Having four subclasses

private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
  1. Buffer capacity is the number of elements it contains. Buffer capacity is never negative, never changed.

  2. Buffer limitation is the index of the first element should not be read or written. Buffer limit is never negative and is never greater than its capacity.

  3. The position of the buffer to be read next index or a write element. The location of the buffer is never negative and never more than its limit.

  4. // Invariants: mark <= position <= limit <= capacity
    

Code Tracking - Detailed

image-20200324205035963

image-20200324205428253

Buffer class and its subclasses in important ways

image-20200324210320538

E.g., by setting the value of position, to read the value of the specified position. You can also modify the limit values ​​and the like.

ByteBuffer

ByteBuffer, is the most common. Binary data.

image-20200324210425809

Three core - Channel Channel Comments

Channel interface Doc source (java11)

image-20200324211700736

basic introduction

image-20200324212539027

image-20200324212207511

Channel subclasses

image-20200324211833203

FileChannel class

image-20200324212248256

Channel application examples

Channel Application Example 1-- local file write data

image-20200325052304319

Example code:

package com.dawa.netty.nio;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class TestNIOFileChannel01 {
    public static void main(String[] args) throws Exception{

        //准备字符串
        String string = "dawa,大娃,Bigbaby";

        //准备输出流.指定输出的文件地址
        FileOutputStream fileOutputStream = new FileOutputStream("dawa.txt");

        //准备Channel管道. 对输出流进行封装,封装为一个channel管道.
        FileChannel fileChannel = fileOutputStream.getChannel();

        //准备一个byte数组, 也就是一个 Buffer数组,来缓存数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        
        //读取数据
        byteBuffer.put(string.getBytes());

      	//这里第一次没有反转,文件里面乱码
        byteBuffer.flip();
      
        //完成写的操作
        fileChannel.write(byteBuffer);

        //关闭流
        fileOutputStream.close();
    }
}

note:

  1. Is FileOutPutStream contains NIO FileChannel
  2. FileChannel of a specific category is: FileChannelImpl

image-20200325052449120

Channel Application Example 2-- local file read data

image-20200325052837040

Code case are as follows

package com.dawa.netty.nio;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

//本地 读文件
public class TestNIOFileChannel02 {
    public static void main(String[] args) throws Exception {

        //读到文件
        File file = new File("dawa.txt");
        FileInputStream fileInputStream = new FileInputStream(file);

        //fileInputStream 包装为 Channel
        FileChannel fileChannel = fileInputStream.getChannel();

        //借助Buffer byte[]缓冲数
        ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());

        //将Channel的数据读入到byteBuffer
        fileChannel.read(byteBuffer);

        System.out.println(new String(byteBuffer.array()));

        fileInputStream.close();

    }
}

Channel Applications 3-- use Buffer to read and write complete file

Similar to the copy operation, using the file completed Channel + Buffer

image-20200325054011127

Code case are as follows

package com.dawa.netty.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

//使用一个Channel完成文件的读写
public class TestNIOFileChannel03 {
    public static void main(String[] args)  throws  Exception{

        FileInputStream fileInputStream = new FileInputStream("dawa.txt");
        FileChannel channel01 = fileInputStream.getChannel();

        FileOutputStream fileOutputStream = new FileOutputStream("erwa.txt");
        FileChannel channel02 = fileOutputStream.getChannel();

        //Buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

        while (true) {
            //这里注意使用Clear操作,不然会进入死循环
            /**
             * public Buffer clear() {
             *         position = 0;
             *         limit = capacity;
             *         mark = -1;
             *         return this;
             *     }
             */
            byteBuffer.clear();

            int read = channel01.read(byteBuffer);
            if (read == -1) {
                break;
            }
            //反转,切换流
            byteBuffer.flip();
            channel02.write(byteBuffer);
        }
						fileInputStream.close();
            fileOutputStream.close();
    }
}

It should be noted that the use of clear operation, reset the basic parameters of the buffer

public Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

Channel Applications 4 - Transferform copy

transferFrom method

    public long transferFrom(ReadableByteChannel src,
                             long position, long count)
        throws IOException
    {
        ensureOpen();
        if (!src.isOpen())
            throw new ClosedChannelException();
        if (!writable)
            throw new NonWritableChannelException();
        if ((position < 0) || (count < 0))
            throw new IllegalArgumentException();
        if (position > size())
            return 0;
        if (src instanceof FileChannelImpl)
           return transferFromFileChannel((FileChannelImpl)src,
                                          position, count);

        return transferFromArbitraryChannel(src, position, count);
    }

Case are as follows

package com.dawa.netty.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

//使用一个Channel完成文件的读写
public class TestNIOFileChannel03 {
    public static void main(String[] args)  throws  Exception{

        FileInputStream fileInputStream = new FileInputStream("dawa.txt");
        FileOutputStream fileOutputStream = new FileOutputStream("erwa.txt");

        FileChannel sourceCH = fileInputStream.getChannel();
        FileChannel destCH = fileOutputStream.getChannel();

        //直接通过通道,完成拷贝
        destCH.transferFrom(sourceCH, 0, sourceCH.size());

        fileInputStream.close();
        fileOutputStream.close();

    }
}

Notes and attention to detail on the Channel Buffer and

image-20200325060447130

  1. Access type must be the same (same access sequence)

    image-20200325061118654

  2. Buffer can be converted to a read-only Buffer

    byteBuffer.asReadOnlyBuffer();
    
   ![image-20200325061059067](https://tva1.sinaimg.cn/large/00831rSTly1gd5rck2zicj31ja0tyh7e.jpg)

3. **MappedBuffer 可以让文件直接在内存(堆外内存)修改,操作系统不需要拷贝一次**

   ![image-20200325061421909](https://tva1.sinaimg.cn/large/00831rSTly1gd5rge5zuaj30oi0b4tad.jpg)

   MappedByteBuffer是抽象类,实际能够操作的类型是 DirectByteBuffer

   > 代码案例如下:

   ```java
   package com.dawa.netty.nio;
   
   import java.io.RandomAccessFile;
   import java.nio.MappedByteBuffer;
   import java.nio.channels.FileChannel;
   
   //使用 MappedBuffer 直接完成文件在内存中的数据修改
   public class MappedBuffer01 {
       public static void main(String[] args) throws Exception {
   
           //获取一个读取文件流
           RandomAccessFile randomAccessFile = new RandomAccessFile("dawa.txt","rw");
   
           //获取指定的Channel
           FileChannel channel = randomAccessFile.getChannel();
   
           //读取模式. 0 代表从0开始, 5代表读取5个字节,也同时意味着只能在内存中操作这5个字节
           MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
   
           //对指定位置进行操作
           mappedByteBuffer.put(0, (byte) 'A');
           mappedByteBuffer.put(2, (byte) 9);
   
           randomAccessFile.close();
           channel.close();
       }
   }
  1. Scattering & Gathering of use

    Scattering : When writing data to the Buffer, Buffer array may be employed sequentially written dispersion []

    Gathering: reading data from Buffer, Buffer array may be employed sequentially read [polymerization]

    Problem solving: when an array is not enough, you can use an array group to perform similar operations

    Code case are as follows: using an array, to complete the client - server reading operation

    package com.dawa.netty.nio;
    
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Arrays;
    
    public class ScatteringGatheringGetPut {
        public static void main(String[] args) throws Exception {
    
            //创建服务器端的
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            //监听端口号
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 7000);
    
            //绑定端口号到服务器端的Channel
            serverSocketChannel.socket().bind(inetSocketAddress);
    
            //创建Buffer数组
            ByteBuffer[] byteBuffers = new ByteBuffer[2];
            byteBuffers[0] = ByteBuffer.allocate(5);
            byteBuffers[1] = ByteBuffer.allocate(3);
    
    
            // 等待连接,获取连接, 并生成客户端的 Channel
            SocketChannel socketChannel = serverSocketChannel.accept();
    
            //假设从 客户端读取 8个字节
            int messageLength = 8;
    
            while (true) {
                //1. 将客户端的数据, 读取
                int byteRead = 0;
                while (byteRead < messageLength) {
                    long l = socketChannel.read(byteBuffers);
                    System.out.println("byteRead = " + byteRead);
                    byteRead += 1;//累积读取的字节数
    
                    //使用流打印,看看当前Buffer里面的position和limit
                    Arrays.asList(byteBuffers).stream()
                            .map(byteBuffer -> "position=" + byteBuffer.position() + ", limit=" + byteBuffer.limit()).forEach(System.out::println);
                }
    
                //将所有的 Buffer反转
                Arrays.asList(byteBuffers).forEach(ByteBuffer::flip);
    
                //2. 将读取到的数据,写回客户端
                int byteWrite = 0;
                while (byteWrite < messageLength) {
                    socketChannel.write(byteBuffers);
                    byteWrite += 1;
                }
    
                //将所有的Buffer进行Clear操作
                Arrays.asList(byteBuffers).forEach(ByteBuffer::clear);
    
                //读完之后,打印出来看看读写文件的长度
                System.out.println("byteRead = " + byteRead + ", byteWrite = " + byteWrite + ", messageLength" + messageLength);
    
            }
    
        }
    }
    

Three core - Selector Selector Detailed

Selector of Doc source (java11)

image-20200325074926300

image-20200325075002638

basic introduction

image-20200325075828311

  1. schematic diagram:

    image-20200325075740264

Select schematic description and characteristics

image-20200325075854846

Selector subclass

image-20200325080430928

Selector class related method

image-20200325080845317

PS: a thread, corresponding to a Selector, by calling each Selector select () method, acquired to represent different SelectionKey Channel obtain a set of Channel can be selected.

Precautions

image-20200327042610721

NIO non-blocking network programming principle analysis chart

NIO nonblocking network-related (Selector, SelectionKey, ServerSocketChannel and the SocketChannel) diagram carded.

image-20200327042415803

The description of FIG.

  1. When the client generates, it will be obtained by SocketChannel ServerSocketChannel.

  2. Selector Selector start listening ... listening select method returns the number of channels have events.

  3. The Selector SocketChannel register to the .register (Selector sel, int ops). Selector can be registered on a plurality SocketChannel.

    SocketChannel parent class method which has registered

    SelectableChannel there is also a method of registration, the use of more

  4. After registration, it returns a SelectionKey, and the association will Selector (collection)

  5. Further each SelectionKey (an event occurs)

  6. Then reverse acquisition SocketChannel by SelectionKey.

    channel SelectionKey class () method

  7. By Channel get the complete business process

NIO non-blocking network programming Quick Start

创建服务器端。
  1. 创建ServerSocketChannel ,并设置非阻塞
  2. 得到一个Selector对象
  3. 绑定一个端口6666.在服务器端监听
  4. 把servrSocketChannel 注册到 selector  关心事件为 SelectionKey.OP_ACCEPT
  5. 循环等待客户端连接
  	//这里我们等待一秒,如果没有事件发生,返回
  	1. if(selector.selecct(1000)==0){//没有事件发生
      sout("服务器等待了一秒");
      continue;
    }

		//如果返回的值>0,就获取到相关的selectionKey集合
		// 1. 表示已经获取到关注的事件。
		// 2. 通过selectionKeys()返回关注的集合。
		// 3. 通过selectionKeys
		seletor.selectedKeys().var;

		//遍历得到的selectionKeys.
			//1. 获取SelectionKey
			//2. 根据key 对应的通道发生的事件做处理
			//3. 如果是 OP_ACCEPT,有新的客户端连接
					//1. 给该客户端生成一个SocketChannel
					//2. 将SocketChannel 注册到select,关注事件为OP_READ,并关联一个Buffer
			//4. 如果是 OP_READ,读取数据
					//1. 通过key,反向获取对应的channel	
					//2. 获取到该channel关联的buffer 
			//5. 手动从集合中移动单签的SelectionKey,防止重复操作。

创建客户端。
  1. 得到一个网络通道SocketChannel.并设置非阻塞
  2. 提供服务区的IP和端口,连接服务器

Server-side code

package com.dawa.netty.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

// NIO 服务端
public class TestNIOServer {
    public static void main(String[] args) throws Exception {

        //  1. 创建ServerSocketChannel ,并设置非阻塞
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        //  2. 得到一个Selector对象
        Selector selector = Selector.open();
        //  3. 绑定一个端口6666.在服务器端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //  4. 把serverSocketChannel 注册到 selector  关心事件为 SelectionKey.OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 循环,等待客户端连接
        while (true) {
            if (selector.select(1000) == 0) {// 没有事件发生
                System.out.println("服务器端等待1秒,没有客户端连接");
                continue;
            }
            Set<SelectionKey> selectionKeys = selector.selectedKeys();//得到所有被选中的Key
            //循环遍历每一个key,每一个key代表一个事件
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                //根据key对应的事件,做响应的处理
                if (selectionKey.isAcceptable()) {//如果是 Accept事件, 连接事件,则生成对应的客户端Channel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    //将SocketChannel 注册到select,关注事件为OP_READ,并关联一个Buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if (selectionKey.isReadable()) {//如果是读事件
                    //1. 通过key,反向生成Channel
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    //设置非同步NIO
                    socketChannel.configureBlocking(false);
                    //2. 获取到该channel关联的buffer
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    socketChannel.read(buffer);
                    //打印出获取到的Buffer
                    System.out.println("from 客户端:" + new String(buffer.array()));
                }
                //这里一定要记得把处理过的key给移除掉,自己遇到了死循环.
                iterator.remove();
            }
        }
    }
}

Client code

package com.dawa.netty.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

//NIO 客户端
public class TestNIOClient {
    public static void main(String[] args) throws Exception {

        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        //服务器端的IP和端口
        InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 6666);

        if (!socketChannel.connect(socketAddress)) {
            System.out.println("连接失败,但是可以干其他事情,非阻塞");
            while (!socketChannel.finishConnect()) {
                System.out.println("在连接完成之前,我一直干其他的事情");
            }
        }

        String string = "hello,dawa";
        socketChannel.write(ByteBuffer.wrap(string.getBytes()));
        System.in.read();
    }
}

SelectionKey

Selector.keys () is to list all of the key.

Selector.selectedKeys () is to list all the selected key.

These two are not the same.

image-20200327063028759

image-20200327064315095

ServerSocketChannel

image-20200327064402970

SocketChannel

image-20200327064850861

Application examples NIO network programming - a group chat system

Functional Schematic

image-20200327065308315

  1. The first to write server-side
    1. The server initiates and listens 6667
    2. The server receiving the client message, and to implement forwarding [on-line and off-line processing]
  2. Write a client
    1. connect to the server
    2. Send a message
    3. Accept the Server Message

Server-side code

  1. Constructor to initialize

image-20200327065929587

  1. Listener method
    Listen ()
    image-20200327070215028
    inside the loop wording:
    image-20200327070542182

The method of reading data which:

image-20200327071122366

image-20200327071218197

try catch off-line processing is completed
image-20200327071738717

Methods forwarded to other clients inside

image-20200327071538520

Client code

  1. Constructor to initialize

    image-20200327072218159

  2. A message to the server

    image-20200327072357745

  3. Read reply message from the server

    image-20200327072652113

    image-20200327072740228

Start the client and server side

  1. Start the client's method

    image-20200327073043538

    image-20200327073116852

  2. Start the server-side method

    image-20200327073200172

His own encoding: Implement Group

Client code

package com.dawa.netty.nio.group;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;

//客户端
public class GroupCharClient {

    private SocketChannel socketChannel;
    private static final int PORT = 6667;
    private static final String HOST = "127.0.0.1";
    private Selector selector;
    private String userName;

    public GroupCharClient() throws IOException {
        selector = Selector.open();
        //连接服务器
        socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        userName = socketChannel.getRemoteAddress().toString().substring(1);
    }

    //发送消息
    public void sendMessage(String message){
        message = userName + "说" + message;
        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
        try {
            socketChannel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //读取服务器端发来的消息
    public void readMessage() {
        try {
            int readChannels = selector.select();
            if (readChannels > 0) {//有可用的通道
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        socketChannel.read(buffer);

                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                    }
                }
            } else {
                //没有可用的通道
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        GroupCharClient groupCharClient = new GroupCharClient();

        new Thread(() -> {
            while (true) {
                groupCharClient.readMessage();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextLine()) {
            String message = scanner.nextLine();
            groupCharClient.sendMessage(message);
        }
    }

}

Server-side code

package com.dawa.netty.nio.group;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 服务器端代码
 */
public class GroupChatServer {
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private static final int PORT = 6666;

    public GroupChatServer() {
        try {
            //得到选择器
            selector = Selector.open();
            //绑定端口
            serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(PORT));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //服务器端监听的方法
    public void listen() {
        try {
            //循环监听
            while (true) {
                int count = selector.select();
                if (count > 0) {
                    Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();
                    while (selectionKeyIterator.hasNext()) {
                        //取出Key
                        SelectionKey key = selectionKeyIterator.next();

                        //判断事件
                        if (key.isAcceptable()) {//监听访问
                            //key 转Channel
                            SocketChannel channel = serverSocketChannel.accept();
                            SocketAddress remoteAddress = channel.getRemoteAddress();
                            System.out.println(remoteAddress + ":上线了");
                        }
                        if (key.isReadable()) {//读取事件
                            //处理读
                            readData(key);
                        }
                        //移除已经处理的key
                        selectionKeyIterator.remove();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //读数据
    public void readData(SelectionKey key) {
        SocketChannel channel = null;
        try {
            //根据key,取得Channel
            channel = (SocketChannel) key.channel();
            channel.configureBlocking(false);
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int read = channel.read(buffer);
            if (read > 0) {
                String message = new String(buffer.array());
                System.out.println("from: 客户端" + message);

                // 向其他用户,转发消息
                sendMessageToOtherCLient(message,key);
            }

        } catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + " :离线了");
                key.cancel();
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    //向其他用户转发消息
    public void sendMessageToOtherCLient(String message,SelectionKey self){
        System.out.println("服务器转发消息ing");

        selector.keys().forEach(key -> {
            //根据Key,取出对应的SocketChannel.或者是ServerSocketChannel
            Channel targetChannel = key.channel();
            //排除自己
            if (targetChannel instanceof SocketChannel && targetChannel != self) {
                //转型
                SocketChannel dest = (SocketChannel) targetChannel;
                //Buffer
                ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
                try {
                    dest.write(buffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    public static void main(String[] args) {
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}

NIO and zero-copy

What is a zero-copy?

Zero-copy, means that no CPU copy, rather than not copied. From the point of view of the operating system.

basic introduction

image-20200330051538095

Traditional IO read and write data

image-20200330051657640

FIG conventional IO model, state switching: switching user mode and kernel mode: 4 copy, switch 3

MMAP optimization

image-20200330052042194

Mmap Optimization: 3 copy, switch 3

Copy DMA: direct memory accect: Direct Memory Access

sendFile optimization

image-20200330052351584

sendFile Optimization: three copies, switching twice

Zero-copy

Zero-copy, means that no CPU copy, rather than not copied. From the point of view of the operating system.

image-20200330052437619

Here there is also a copy of CPU: kernel buffer-> socket buffer, however, very few copies of the information, such as length, offset, low consumption, can be ignored

Zero copy: 2 copies, switching twice.

image-20200330052906579

Zero-copy is an important means to optimize our network during transmission.

mmap and sendFile difference:

image-20200330052955634

NIO zero-copy case

image-20200330053238276

Traditional IO streams Case

image-20200330053408571

image-20200330053600538

Time consuming traditional IO: 60 ms

Zero-copy case. (NIO)

transferTo subbing layer is zero-copy

image-20200330055530665

image-20200330055035445

Processed zero copy NIO time: 20 ms

Java AIO programming

Java AIO basic introduction

image-20200330055721122

Here temporarily depth extension.

BIO, NIO, AIO Comparison Table

image-20200330055912676

Guess you like

Origin www.cnblogs.com/bigbaby/p/12596350.html