Sorting out NIO basic knowledge points -- except selector

The JVM reads the data model


Program execution efficiency is more determined by I/O efficiency

Need to wait for data transmission

It is the insufficient efficiency of JVM in I/O that leads to the reduction of program efficiency. In the operating system, large pieces of data can be read directly from the hardware, and the JVM I/O prefers small pieces of data reading, which is equivalent to the operating system treating a large truck as transporting a lot of data. The JVM I/O likes to process these data one shovel at a time.

NIO is introduced into JDK4 to meet the I/O requirements of Java programs to the greatest extent

  • java.nio package, which defines various Buffer related classes
  • java. nio. The Channel package contains classes related to Channel and Selector
  • java.nio.charset package, class related to character set

There are three core components in NIO: channel, buffer and selector

What is NIO

The traditional IO is stream oriented. One or more of itself can be read from the stream each time. It can only be read backward and cannot move forward. NIO is buffer oriented. Reading data into a buffer can move forward / backward in the buffer, which increases the flexibility of the program.

In NIO, all data needs to be transmitted through the Channel. The Channel can directly map a piece of data to memory. The Channel is bidirectional and can not only read data. You can also save data. The program cannot directly read the Channel. The Channel only interacts with the Buffer buffer.

If the thread is blocked during IO flow, when calling read()/write() to read and write data, the thread will block until the data is read or completely written. In the process of reading and writing, the thread cannot do other tasks.

NIO is not thread blocked. When a thread reads data from a Channel, if there is no available data in the Channel, the thread is not blocked and can be used for other tasks

Buffer

buffer attribute

Buffer buffer is actually an array. It wraps the contents and information of the array into a buffer object, which provides a set of methods to access these information

Important properties of buffer:

1.capacity: refers to how many data can be stored in the Buffer. The capacity is specified when creating the Buffer buffer. It cannot be modified after creation. If the Buffer is full, it needs to be emptied before data can be written.

2.position indicates the current position, that is, the write / read position of the Buffer. Just after the Buffer object is created, position is initialized to 0. When a data is written, position moves a cell to the back, and its maximum value is capacity-1

When the Buffer is switched from write mode to read mode, the position will be reset to 0. The data will be read from the beginning of the Buffer. Every time a data is read, the position will move one unit back

2.limit upper limit: refers to the first position that cannot be read or written The cells behind the upper limit can neither read nor write. In the write mode of Buffer buffer, limit indicates how many data can be written; In read mode, limit indicates the maximum number of data that can be read.

3.mark mark: set a mark position. You can call the mark method to set the mark at the position position. When you call the reset() method, you can set the position as the position of the mark mark.

0<=mark<=position<=limit<=capacity

Buffer common API s

The key buffers in NIO include Byte Buffer, charbuffer, doublebuffer, floatbuffer, intbuffer, longbuffer and shortbuffer.

These buffers cover all the basic types that can be sent through I/O: byte, char, double, and flaot Int.long, short, etc. In fact, Byte Buffer and CharBuffer are mostly used.

  • Each buffer has a static method allocate(capacity), which can create a buffer with a specified capacity;
  • The put() method is used to store data into the buffer;
  • The get() method can read data from the buffer;
  • When there is still unread data in the Buffer, you can call the compact method to compress, copy all unread data to the starting position of the Buffer, and set the position after the last unread element Set the limit property to capacity
  • The capacity() method returns the size of the buffer
  • hasRemaining() determines whether there is data that can be processed after the current position, that is, whether there is data that can be processed between position and limit.
  • limit() returns the position of the upper limit
  • mark() sets the flag position of the buffer. This value can only be between 0 – position. Later, you can set position as the position of mark through reset()
  • position() can return the current position of position
  • remaining() returns the amount of data between the current position and limit
  • The reset() method can set position to the mark bit
  • rewind(): set position to 0 and cancel the mark flag bit
  • clear() clears the buffer. It only modifies the position flag bit 0 and sets the limit to capacity. The data in the buffer still exists
  • The flip() method can switch the buffer from write mode to read mode, first set the limit to position, and then set the position to 0;

Demonstration of Buffer API usage

public class BufferAPI
{
    public static void main(String[] args) {
        //Use character buffer -- a Chinese character and an English character are the same character size, but the byte size is different
        CharBuffer charBuffer=CharBuffer.allocate(12);
        curPosition(charBuffer);//position=0,limit=12

        //Store data in buffer
        charBuffer.put("large");
        charBuffer.put("Suddenly");
        charBuffer.put("Leisurely");
        curPosition(charBuffer);//position=3,limit=12

        //Call flip to switch the buffer from write mode to read mode
        charBuffer.flip();
        curPosition(charBuffer);//position=0,limit=3

        //Read data from buffer
        System.out.print(charBuffer.get());
        System.out.print(charBuffer.get());
        System.out.print(charBuffer.get());
        curPosition(charBuffer);//position=3,limit=3

        charBuffer.clear();
        curPosition(charBuffer);//position=0,limit=12,mark=-1

        //Put data into buffer
        charBuffer.put("Small");
        charBuffer.put("Friend");
        charBuffer.put("friend");
        curPosition(charBuffer);//position=3,limit=12

        //Switch to write mode
        charBuffer.flip();//position=0,limit=3,mark=-1

        //set mark
        charBuffer.mark();//mark=0

        //Read another character
        System.out.println(charBuffer.get());
        curPosition(charBuffer);//position=1,limit=3


        //Call reset() to reset position to mark position
        charBuffer.reset();
        curPosition(charBuffer);//position=0,limit=3

        //Call compact to copy the unread data to the position of 0
        charBuffer.compact();
        curPosition(charBuffer);

        //Clear is called to clear, but position=0, limit=capacity, and the data still exists
        charBuffer.clear();
        curPosition(charBuffer);

        //Read data
        System.out.println(charBuffer);
        curPosition(charBuffer);

        //Read one by one -- print the contents between position and limit one by one
        while(charBuffer.hasRemaining())
        {
            System.out.println(charBuffer.get());
        }
        curPosition(charBuffer);

    }

    //Print specific information about the current location
    public static void curPosition(CharBuffer charBuffer)
    {
        System.out.println("Current buffer capacity capacity: "+charBuffer.capacity()
                +" Current buffer limit position: "+charBuffer.limit()+
                " Current buffer position Pointer position: "+charBuffer.position());
    }
}

Output results:

Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 0
 Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 3
 Current buffer capacity capacity: 12 Current buffer limit position: 3 Current buffer position Pointer position: 0
 Large flicker current buffer capacity capacity: 12 Current buffer limit position: 3 Current buffer position Pointer position: 3
 Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 0
 Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 3
 Small
 Current buffer capacity capacity: 12 Current buffer limit position: 3 Current buffer position Pointer position: 1
 Current buffer capacity capacity: 12 Current buffer limit position: 3 Current buffer position Pointer position: 0
 Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 3
 Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 0
 children         
Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 0
 Small
 Friend
 friend
 
 
 
 
 
 
 
 
 
Current buffer capacity capacity: 12 Current buffer limit position: 12 Current buffer position Pointer position: 12

Buffer batch data transfer

The use of buffer is to improve the data transmission efficiency. The efficiency of reading and writing one character or one byte at a time is not high. Batch operations can be carried out. With the help of array, a piece of data in the buffer can be read into the array, and some contents of the array can also be saved into the buffer

public class BufferAPI
{
    public static void main(String[] args) {
        //Use character buffer -- a Chinese character and an English character are the same character size, but the byte size is different
        CharBuffer charBuffer=CharBuffer.allocate(12);
        //Save string to buffer buffer
        charBuffer.put("123456654321");
        //Reverse read / write mode
        charBuffer.flip();//position=0,limit=12
        System.out.println(charBuffer);//Only the content from position to limit will be output

        //Receive the data in the buffer through the character array
        char[] dst=new char[8];
        //Call the get() method to read the data in the buffer into the character array
        //Note that the size is always fixed during batch transmission. If the transmission size is not specified, it means that the array is filled
        CharBuffer charBuffer1 = charBuffer.get(dst);
        System.out.println(new String(dst));
        //The buffer returned by the get method is the same as that defined at the beginning
        System.out.println(charBuffer);
        System.out.println(charBuffer1);//Data remaining in buffer

        //Continue reading the contents of the buf buffer into the character array
        //An exception is thrown when the buffer does not have enough data to fill the entire array
         //charBuffer.get(dst);//BufferUnderflowException

        //When reading buffer data in batch, remember to query the remaining amount of buffer
        //When filling a large array with data from a small buffer, the length of the remaining buffer should be specified
        //Transfer the remaining data in the buf buffer to the position starting from 0 of the dst array
      charBuffer.get(dst,0,charBuffer.remaining());
        System.out.println(dst);

        //Loop reads data from buffer
        charBuffer.clear();
        dst=new char[8];
        while(charBuffer.hasRemaining())
        {
            int min = Math.min(dst.length, charBuffer.remaining());
            charBuffer.get(dst,0,min);
            System.out.println(dst);
        }

        curPosition(charBuffer);
        //When storing data into the buffer, you should pay attention to the shortage of buffer space, otherwise an exception will be thrown
        //Adjust the position of the position pointer
        charBuffer.position(10);
        //You can also store two characters at this time
        char[] test={'a','b','c','d'};
        charBuffer.put(test,0,charBuffer.remaining());
        charBuffer.clear();
        System.out.println(charBuffer);
    }

    //Print specific information about the current location
    public static void curPosition(CharBuffer charBuffer)
    {
        System.out.println("capacity: "+charBuffer.capacity()
                +" limit: "+charBuffer.limit()+
                " position: "+charBuffer.position());
    }
}

There are two ways to create a buffer

1. Allocate and create buffer. allocate() method allocates a private array with specified capacity to store elements

2. The wrapping operation creates a buffer, which uses the provided array as the storage space to store the data in the buffer, and no other space is allocated

public class BufferAPI
{
    public static void main(String[] args) {
        //There are two ways to create a buffer
        //1.allocate()
        CharBuffer charBuffer1=CharBuffer.allocate(12);
        //2.wrap()
        CharBuffer charBuffer3=CharBuffer.wrap("Huyou ");
        curPosition(charBuffer3);

        char[] array=new char[12];
        CharBuffer charBuffer2=CharBuffer.wrap(array);
        curPosition(charBuffer2);
        
    }

    //Print specific information about the current location
    public static void curPosition(CharBuffer charBuffer)
    {
        System.out.println("capacity: "+charBuffer.capacity()
                +" limit: "+charBuffer.limit()+
                " position: "+charBuffer.position());
    }
}

Calling the put method to put data into the buffer will directly affect the array. Similarly, any modification to the array will directly affect the buffer object

        char[] array=new char[12];
        CharBuffer charBuffer2=CharBuffer.wrap(array);
        curPosition(charBuffer2);

        charBuffer2.put("Hey, hey, hey");
        System.out.println(array);
       curPosition(charBuffer2);

        array[4]='Small';
        array[5]='Friend';
        array[6]='friend';
        System.out.println(charBuffer2);
        curPosition(charBuffer2);

The hasArray() method determines whether there is an accessible backup array

If hasArray() returns true, you can return a reference to the backup array used by the buffer object through array()

     char[] array=new char[12];
        CharBuffer charBuffer2=CharBuffer.wrap(array);
        charBuffer2.put("Hey, hey, hey");

        if(charBuffer2.hasArray())
        {
            char[] array1 = charBuffer2.array();
            System.out.println(array1);
        }

Replication and separation of buffers

Copy operation

public class BufferAPI
{
    public static void main(String[] args)
    {
       CharBuffer charBuffer=CharBuffer.wrap("123456");
       //Buffer copy
        CharBuffer duplicate = charBuffer.duplicate();
        System.out.println(duplicate);
        //As can be seen below, the two buffers only use the same array, and the two buffer objects are different objects
        //The two buffers actually refer to the same array
        duplicate.position(4);
        curPosition(duplicate);
        curPosition(charBuffer);
    }

    //Print specific information about the current location
    public static void curPosition(CharBuffer charBuffer)
    {
        System.out.println("capacity: "+charBuffer.capacity()
                +" limit: "+charBuffer.limit()+
                " position: "+charBuffer.position());
    }
}


The essence of buffer copying is that two different buffer objects share a buffer array

Separation operation

Separate the buffer, and the slice() method creates a new buffer according to the [position.limit) interval

public class BufferAPI
{
    public static void main(String[] args)
    {
       CharBuffer charBuffer=CharBuffer.wrap("123456");
       charBuffer.position(5);
        CharBuffer slice = charBuffer.slice();
        curPosition(charBuffer);
        curPosition(slice);
    }

    //Print specific information about the current location
    public static void curPosition(CharBuffer charBuffer)
    {
        System.out.println("capacity: "+charBuffer.capacity()
                +" limit: "+charBuffer.limit()+
                " position: "+charBuffer.position());
    }
}

Direct byte buffer

The data processed by the hard disk and the operating system are 01 binary. Only ByteBuffer byte buffer in the buffer is qualified to participate in I/O operations.

A Channel channel can only use ByteBuffer as its parameter.

Direct byte buffer is usually the best choice for I/O operation. If indirect byte buffer is used, it may cause performance loss. If an indirect byte buffer is passed to the channel, the channel may first create a temporary direct byte buffer and copy the contents of the indirect buffer to the temporary direct byte buffer to perform the underlying I/O operation.

Direct buffer is the best choice for I/O. the cost of creating direct buffer may be higher than that of creating indirect buffer. The memory used by direct buffer is allocated by calling the local operating system code, bypassing the JVM stack. Now the JVM may perform buffer optimization

The ByteBuffer.allocateDirect() method creates a direct byte buffer

Channel

Channel is a new I/O access method, which is used to transfer data between the byte buffer and the entity on the other side of the channel (which can be a file or a Socket).

The Channel can read and write data in both directions, and can also realize asynchronous reading and writing.

The program cannot directly access the channel. The channel can only interact with the Buffer buffer, that is, read the data in the channel into the Buffer buffer, and the program reads the data from the Buffer;

During the write operation, the program writes the data into the Buffer, and then writes the data from the Buffer into the Channel

Common channels:

  • FileChannel: the channel for reading and writing files
  • SocketChannel/ServerSocketChannel: read and write data in Socket
  • Datagram channel: read and write network data through UDP.

be careful:

1. The channel cannot be created by the construction method

2. Channels cannot be reused. Once a channel is created, it means that a specific I/O service has established a connection. Once the channel is closed, the connection will disappear

3. The channel can be operated in the default blocking mode or set to non blocking mode

Scatter and Gather

Scatter and gather are important functions provided by channels (sometimes also called vector I/O)

Scatte,Gather means to implement a simple I/O operation in multiple buffers.

Scatter refers to reading data from the channel and writing these data to multiple Buffer buffers in order

Gather refers to writing data from multiple Buffer buffers to the same Channel during write operations


Scatter and Gather are often used in scenarios where the transmitted data needs to be processed separately

For example, Scatter reads data from one Channel and stores it in multiple buffers:

//Sample pseudo code
ByteBuffer c1=ByteBuffer.allocate(3);
ByteBuffer c2=ByteBuffer.allocate(3);
ByteBuffer[] bufArray={buf1,buf2}
Channel.read(bufArray);

The Read() method reads data from the Channel and stores it in the buffer in the order in the array.

Note that the buf1 buffer must be full before it can be written to the buf2 buffer.

The size of data processed with Scatter/Gather is fixed

It is not suitable for uncertain data size

FileChannel

FileChannel calls the getChannel() method through RandomAccessFile, FileInputStream and fileoutputstream objects.

Although FileChannel is bidirectional and can be read or written, the channel obtained from FileInputStream stream stream can only be read and cannot be written. If writing is performed, an exception will be thrown; the channel obtained from FileOutputStream stream can only be written and cannot be read; if the accessed file is read-only, it cannot be written.

FileChannel is thread safe, but not all operations are multithreaded. For example, operations that affect channel location or file size are single threaded.

1. Memory mapping file

The map() method of FileChannel can map the entire file on the disk to the virtual memory of the computer, and package this virtual memory as a MappedByteBuffer object. Note: Currently, when a file is mapped to the virtual memory, the contents of the file are usually not read from the hard disk to the memory.

Copy files through memory mapping:

public class BufferAPI
{
  //The path of the current project
  private static final String project_path=System.getProperty("user.dir")+System.getProperty("file.separator");
  //source file
  private final static File src=new File(project_path+"src.txt");
  //Purpose document
  private final static File dst=new File(project_path+"tar.txt");

    public static void main(String[] args)
    {
       //Put Src Txt reads tar.txt in a memory mapped manner txt

      //Use try resources
      try(
              FileChannel fin=new FileInputStream(src).getChannel();
              FileChannel fout=new FileOutputStream(dst).getChannel();
              ) {
          //Map the data in the fin channel to virtual memory
          //Parameter 1: mapping mode, readable or writable
          //Parameter 2, parameter 3: how many bytes are mapped from a certain location
          MappedByteBuffer mapBuffer = fin.map(FileChannel.MapMode.READ_ONLY, 0, src.length());
          //Outputs the data in the buffer to the fout channel
          fout.write(mapBuffer);

          //You can also print the contents in the buffer
          mapBuffer.flip();//Switch to read mode
          //Create character set
          Charset charset = Charset.defaultCharset();
          //Use the default character set to convert the bytes in buf into characters
          CharBuffer decode = charset.decode(mapBuffer);
          System.out.println("Decoded buffer: "+decode);
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
    }
}

2. Bidirectional transmission of filechannel

The channel obtained by using RandomAccessFile is bidirectional

Set tar Txt file, and then append it to the end of the file again

public class BufferAPI
{
  //The path of the current project
  private static final String project_path=System.getProperty("user.dir")+System.getProperty("file.separator");

    public static void main(String[] args)
    {
        //Use try resources
      try(
              RandomAccessFile dst=new RandomAccessFile(project_path+"tar.txt","rw");
              FileChannel fout=dst.getChannel();
              )
      {
          //Map the data in channel to virtual memory
          MappedByteBuffer mappedByteBuffer = fout.map(FileChannel.MapMode.READ_ONLY, 0, dst.length());
          //Set the position operation of the channel
          fout.position(dst.length());
          //Write buffer data to channel
          fout.write(mappedByteBuffer);
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
    }
}


channel has only position, but not limit and capacity, because these two attributes are for buffers

3. When filechannel reads and writes files, the buffer is fixed in size

public class BufferAPI
{
  //The path of the current project
  private static final String project_path=System.getProperty("user.dir")+System.getProperty("file.separator");
    //source file
    private final static File src=new File(project_path+"src.txt");
    //Purpose document
    private final static File dst=new File(project_path+"dst.txt");

    public static void main(String[] args)
    {
        //Use try resources
      try(
         FileChannel fin=new FileInputStream(src).getChannel();
         FileChannel fout=new FileOutputStream(dst).getChannel();
         )
      {
        //Defines a fixed size buffer
          ByteBuffer byteBuffer=ByteBuffer.allocate(48);
          //Constantly read data from the channel and put it into the buffer
          while(fin.read(byteBuffer)!=-1)
          {
              byteBuffer.flip();//The buffer is switched from write mode to read mode
              while(byteBuffer.hasRemaining())
              {
                  //Read data from the buffer and put it into the channel
                  fout.write(byteBuffer);
              }
          }
          byteBuffer.clear();
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
    }
}

The file does not exist, it will be created

4. Transmission between channels

It is often necessary to batch transfer files from one location to another. Channel to channel transmission can be used directly without intermediate buffer to transfer data

Note that only FileChannel supports Channel - to - channel transmission

Channel to channel transmission is very fast. Some operating systems can directly transmit data without using user space.

public class BufferAPI
{
  //The path of the current project
  private static final String project_path=System.getProperty("user.dir")+System.getProperty("file.separator");
    public static void main(String[] args)
    {
        //Use try resources
      try(
              RandomAccessFile src= new RandomAccessFile(project_path+"src.txt", "rw");
              FileChannel fin=src.getChannel();
            FileChannel fout=new FileOutputStream(project_path+"dst.txt").getChannel();
              )
      {
          //transferTo -- transfers the data in the current channel to another channel
          //Parameter 1: from which position of the current channel is data transmitted
          //Parameter 2: how many bytes of data are transmitted
          //Parameter 3: destination channel
          //fin.transferTo(0,src.length(),fout);

          //transform
          //Parameter 1: source channel
          //Parameters 2 and 3 correspond to the above: how many bytes are transferred from a certain position of the original channel to the current channel
        fout.transferFrom(fin,0,src.length());


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

5.Gather implementation

The attributes and contents of the file are stored in different buffers, and then written to another file



public class BufferAPI
{
  //The path of the current project
  private static final String project_path=System.getProperty("user.dir")+System.getProperty("file.separator");
  //Newline character
  private static final String line_separator=System.getProperty("line.separator");
  //Of the current class java file
  private static final String curJavaFilePath=project_path+"src\\com\\NIO\\BufferAPI.java";

    public static void main(String[] args)
    {
        File file=new File(curJavaFilePath);

        //Get file absolute path
        String absolutePath = file.getAbsolutePath();
        //Time when the file was last modified
        long lastModified = file.lastModified();
        String formatDate = formatDate(lastModified);
        //Store file attributes in buffer
        String headerText="file path: "+absolutePath+"  "+line_separator
                +"file modified: "+formatDate+line_separator;
        ByteBuffer headerBuffer=ByteBuffer.wrap(headerText.getBytes());

        //File content type
        ByteBuffer contentBuffer=ByteBuffer.allocate(1024);

        //Create a buffer array
        ByteBuffer[] gather={headerBuffer,contentBuffer,null};

        String contentType="unkonwn";
        Long length=-1l;

        //Map files to virtual memory
        try (FileChannel channel = new FileInputStream(file).getChannel();)
        {
            //Map files to virtual memory
            MappedByteBuffer fileData = channel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            //Save the contents of the file mapped to memory into the gather array
            gather[2]=fileData;

            //file length
            length=file.length();
            //Returns the type of the current file
            String name = URLConnection.guessContentTypeFromName(file.getAbsolutePath());
            StringBuilder stringBuilder=new StringBuilder();
            String string = stringBuilder.append("file Len: " + length+line_separator)
                    .append("contentType: " + name+line_separator).toString();
            gather[1]=contentBuffer.put(string.getBytes());
            //Reverse read / write mode
            contentBuffer.flip();
            //Write the contents of the gather array to the file
            FileChannel fileChannel = new FileOutputStream(project_path + "dst.txt").getChannel();
            //If the return value of write is 0, it means that the writing is finished. If it is not 0, there is still data to be written
            while(fileChannel.write(gather)>0)
            {}
            //When you're done, close the channel
            fileChannel.close();
        } catch (FileNotFoundException e)
        {
            addErrorMessageToFile(gather,e.getMessage());
        } catch (IOException e) {
            addErrorMessageToFile(gather,e.getMessage());
        }
    }

    //If there is an exception in the file operation, save the exception to the error log
    public static void addErrorMessageToFile(ByteBuffer[] gather,String error)
    {
        ByteBuffer wrap = ByteBuffer.wrap(error.getBytes());
        gather[2]=wrap;
    }


   //Time formatting
   public static String formatDate(Long epochSecond)
   {
         Date date=new Date(epochSecond);
         DateFormat dateFormat = new SimpleDateFormat("yyyy year MM month dd day HH:mm:ss");
       String format = dateFormat.format(date);
       return format;
   }
}

effect:

socketChannel and serverSocketChannel

ServerSocketChannel can listen for new TCP connection channels

SocketChannel is a channel that connects to a TCP network socket

Server side code - serverSocketChannel implementation

public class DHYServerSocketChannel
{
    //Server side port number
    private static Integer Default_Port=80;

    public static void main(String[] args) throws IOException, InterruptedException {
        //Establish a channel for an unbound serverbasket server
        java.nio.channels.ServerSocketChannel ssc = java.nio.channels.ServerSocketChannel.open();
        //ServerSocketChannel does not have a bind binding method. You need to first obtain the ServerSocket object through the socket() method, and then bind the port number
        ssc.socket().bind(new InetSocketAddress(Default_Port));
        //Set the channel to non blocking mode. When there is no socket and no incoming connection, the accept () method returns null
        ssc.configureBlocking(false);

        while(true)
        {
            java.nio.channels.SocketChannel socketChannel = ssc.accept();
            //If there is no connection, socketChannel=null
            if(socketChannel==null)
            {
                Thread.sleep(2000);
            }
            else
            {
                //Connection incoming
                //Send a greeting to the client first
                ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                byteBuffer.put("Hello, this is the server".getBytes());
                byteBuffer.flip();
                socketChannel.write(byteBuffer);
                //Then read the content sent from the client
                System.out.println("client["+socketChannel.socket().getRemoteSocketAddress()+"]");
                byteBuffer.clear();
                //Read the data sent by the client and save it in the buffer
                socketChannel.read(byteBuffer);
                byteBuffer.flip();//Switch to read mode
                //decode
                Charset charset = Charset.defaultCharset();
                //A character buffer is produced after decoding
                CharBuffer decode = charset.decode(byteBuffer);
                System.out.println("Client message: "+decode);
                //Close the connection between the server and the client
                socketChannel.close();
                break;
            }
        }
    }
}

Client side code - SocketChannel implementation

public class DHYSocketChannel
{
    //IP address of serverSocket --- that is, the IP address of the server
    private  static String HOST_IP="localhost";
    //serverSocket registered port number
    private static Integer HOST_PORT=80;
    public static void main(String[] args) throws IOException {
        InetSocketAddress address=new InetSocketAddress(HOST_IP, HOST_PORT);
        //Create an unconnected SocketChannel
        SocketChannel socketChannel = SocketChannel.open();
        //Establish a connection to the server
        socketChannel.connect(address);
        //TCP connection takes some time, and the establishment of two connections requires packet dialogue
        //Call the finishConnect() method to complete the connection process. If no connection is successful, false is returned
        while(!socketChannel.finishConnect())
        {
            System.out.println("Waiting for connection...");
        }
        System.out.println("Connection succeeded");
         //Send message to server
        ByteBuffer buffer=ByteBuffer.wrap("Hello, this is the client".getBytes());
        while(buffer.hasRemaining())
        {
            socketChannel.write(buffer);
        }
        //Get the message sent by the server to the client
        InputStream inputStream = socketChannel.socket().getInputStream();
        ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);//The channels utility class obtains channels
        buffer.clear();
        readableByteChannel.read(buffer);
        buffer.flip();//Switch read mode
        //decode
        Charset charset = Charset.defaultCharset();
        CharBuffer decode = charset.decode(buffer);
        System.out.println(decode);
    }
}


DatagramChannel

Datagram channel is a channel for sending and receiving UDP packets. Unlike TCP protocol, UDP sending does not connect or confirm whether data is received.

ByteBuffer receiveBuffer = ByteBuffer.allocate(64);
receiveBuffer.clear();
SocketAddress receiveAddr = server.receive(receiveBuffer);

SocketAddress can obtain the ip, port and other information of the contract. Use toString to view it. The format is as follows

/127.0.0.1:57126

connect

udp does not have a real connection. The connection here is to receive and send data packets to a specific service address with read and write.

client.connect(new InetSocketAddress("127.0.0.1",10086));
int readSize= client.read(sendBuffer);
server.write(sendBuffer);

Read () and write() can only be used after connect(), otherwise notyetconnected exception will be thrown. When receiving with read(), if no packet is received, a PortUnreachableException exception will be thrown.

Data receiver

public class DHYDatagramReceiver
{
    public static void main(String[] args) throws IOException, InterruptedException {
        //Create an unbound channel
        DatagramChannel datagramChannel=DatagramChannel.open();
        //Bind a port number
        datagramChannel.bind(new InetSocketAddress(80));
        //Set to non blocking
        datagramChannel.configureBlocking(false);

        //Receive keyboard data
        Scanner sc=new Scanner(System.in);
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //Judge whether data is received
         while(true)
         {
             //Receive data first
             buffer.clear();
             //Receive udp packets through receive() -- receive data from the channel and put it into the buffer
             InetSocketAddress receive = (InetSocketAddress) datagramChannel.receive(buffer);
             //Judge whether data is received
             if(receive==null)
             {
                 Thread.sleep(2000);
                 continue;
             }
             System.out.print("Data from: "+
                     receive);
             //Switch read mode
             buffer.flip();
             String s = new String(buffer.array(), 0, buffer.limit());
             System.out.println(" :"+receive.getPort()+"---->"+s);

             //send data
             String text=sc.nextLine();
             //Data sent, to whom
             datagramChannel.send(ByteBuffer.wrap(text.getBytes()),receive);
         }

    }
}

Data sender

public class DHYDatagramSender
{
    public static void main(String[] args) throws IOException {
        //Create unbound channel
        DatagramChannel datagramChannel = DatagramChannel.open();
        datagramChannel.configureBlocking(false);

        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext())
        {
            String nextLine = scanner.nextLine();
            byteBuffer.clear();
            byteBuffer.put(nextLine.getBytes());
            byteBuffer.flip();
            //Data sent, to whom
            datagramChannel.send(byteBuffer,new InetSocketAddress("localhost",80));

            //receive data 
            byteBuffer.clear();
            SocketAddress receive = datagramChannel.receive(byteBuffer);
            while (receive==null)
            {
                receive=datagramChannel.receive(byteBuffer);
            }
            byteBuffer.flip();
            System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
        }
    }
}

Pipe

The pipe pipe is used for one - way data connection between two threads

pipe has a source channel and a sink channel

Create pipe: pipe = pipe open();

To write data to the pipeline, you first need to access the sink channel

Pipe.SinkChannel sc=pipe.sink();

sc.write(buffer);

Reading data needs to go through the source channel

Pipe.SourceChannel source=pipe.source();

source.read(buffer);

Use Demo:

        public static void test() throws IOException {
            // 1. Obtain pipeline
            Pipe pipe = Pipe.open();

            // 2. Write buffer data to the pipeline
            // 2.1 get a channel
            Pipe.SinkChannel sinkChannel = pipe.sink();
            // 2.2 defining buffers
            ByteBuffer buffer = ByteBuffer.allocate(48);
            buffer.put("send data".getBytes());
            buffer.flip(); // Switch data mode
            // 2.3 writing data to pipeline
            sinkChannel.write(buffer);

            // 3. Read data from pipe
            Pipe.SourceChannel sourceChannel = pipe.source();
            buffer.flip();
            int len = sourceChannel.read(buffer);
            System.out.println(new String(buffer.array(), 0, len));

            // 4. Close the pipe
            sinkChannel.close();
            sourceChannel.close();
        }

Demonstrate the use of pipe pipes for data transfer between two threads:

PipedOutPutStream and PipedInputStream are used, which are pipe output stream and pipe input stream respectively

During pipeline communication, thread A writes data to the PipedOutPutStream, and these data will be automatically sent to the corresponding PipedInputStream

Thread B can read data from PipedInputStream

public class DHYPipe
{
    public static void main(String[] args) throws IOException {
        //Create an input flow pipe
        PipedInputStream in=new PipedInputStream();
        //Create output flow pipe
        PipedOutputStream out=new PipedOutputStream();
        //Establish a connection between the input flow pipe and the output flow pipe
        in.connect(out);
        //perhaps
        //out.connect(in);

        //Create thread
        new Thread(new Sender(out)).start();
        new Thread(new Receiver(in)).start();
    }
}

//Sender
class Sender implements Runnable{
 PipedOutputStream out;

    public Sender(PipedOutputStream out) {
        this.out = out;
    }

    @Override
    public void run() {
        //Analog sending data
            try
            {
                for(int i=0;i<10;i++) {
                    out.write(("Hello, No" + i + "A big trick\n").getBytes(StandardCharsets.UTF_8));
                }

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
}

//receiving end
class Receiver implements Runnable
{
 PipedInputStream in;

    public Receiver(PipedInputStream in) {
        this.in = in;
    }

    @Override
    public void run() {
        //receive data 
        byte[] bytes=new byte[1024];

        try {
            int len;
            while((len=in.read(bytes))!=-1)
            {
                System.out.println(new String(bytes,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Use sinkChannel and sourceChannel to complete the demonstration

public class DHYPipe
{
    public static void main(String[] args) throws IOException {
       //Get pipeline
        Pipe pipe=Pipe.open();
        //Create thread
        new Thread(new Sender(pipe.sink())).start();
        new Thread(new Receiver(pipe.source())).start();
    }

}

//Sender
class Sender implements Runnable{
    Pipe.SinkChannel sinkChannel;

    public Sender(Pipe.SinkChannel sinkChannel) throws IOException {
        this.sinkChannel = sinkChannel;
        //Set to non blocking
        sinkChannel.configureBlocking(false);
    }

    @Override
    public void run() {
        //Analog sending data
            try
            {
                sinkChannel.write(ByteBuffer.wrap("I'm a big fool".getBytes()));
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    sinkChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
}

//receiving end
class Receiver implements Runnable
{
 Pipe.SourceChannel sourceChannel;

    public Receiver(Pipe.SourceChannel sourceChannel) {
        this.sourceChannel = sourceChannel;
    }

    @Override
    public void run() {

        try {
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            sourceChannel.read(byteBuffer);
            byteBuffer.flip();
            Charset charset = Charset.defaultCharset();
            CharBuffer decode = charset.decode(byteBuffer);
            System.out.println(decode);
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                sourceChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Arrangement of supplementary knowledge points

HTTP, status code, TCP, UDP and other network protocols

Graphic summary of HTTP, status code, TCP, UDP and other network protocols (continuously updated)

UrlConnection

URLConnection

Channels

Channels

Keywords: Java Back-end NIO

Added by Yegg on Thu, 09 Dec 2021 17:48:27 +0200