A review,
We learned the basic knowledge of NIO programming, and through a small demo to help understand the concept of NIO programming channel, buffer and so on. This article will continue learning about JAVA NIO programming with a small example to help you understand what you’re learning
- Scatter and Scatter buffers
- SocketChannel and ServerSocketChannel usage
- The use of selectors
What is Scatter and Gather?
- Reading from a Channel is when the data is read and written to multiple buffers. Therefore, a Channel “scatters” the data read from a Channel into multiple buffers.
- To gather data into a Channel means to write data from multiple buffers to the same Channel during a write operation. Therefore, a Channel “gathers” data from multiple buffers and sends it to a Channel.
Scatter diagram
The buffer must be filled from the channel before the following buffer is filled, which means that you cannot dynamically adjust the size of each buffer.
The sketch of the Gather
Aggregation and dispersion are opposite forms. When data is written from buffer to channel, only the positon position of buffer to limit position is written, which means that content can be dynamically written into channel.
Three, selector
What is a selector
A Selector is a component in Java NIO that can detect multiple NIO channels and know if the channel is ready for events such as read and write events. In this way, a single thread can manage multiple channels, thus managing multiple network connections and improving efficiency.
Why use a selector
Using a selector allows you to manage multiple channels with a single thread. If multiple channels are managed by multiple threads, switching between threads consumes resources, while a single thread avoids switching between threads.
Common methods of selectors
The method name | function |
---|---|
register(Selector sel, int ops) | Register channels with the selector, and can choose to register the specified events, currently divided into four types; 1.Connect, 2.Accept, 3.Read, 4 |
select() | Block until at least one channel is ready on the event you registered |
selectNow() | Will not block, whatever channel is ready to return immediately |
select(long timeout) | Same as select(), except that it blocks at most timeout milliseconds. |
selectedKeys() | Once the select() method is called and the return value indicates that one or more channels are ready, the ready channels in the selectedKey Set can be accessed by calling the Selector selectedKeys() method |
wakeUp() | You can make an object blocked by calling SELECT () return without blocking. |
close() | Calling its close() method after a Selector is used closes that Selector and invalidates any SelectionKey instances registered with that Selector. The channel itself does not close |
Four, in actual combat
Actual combat requirement description
Encoding client and server, the server can accept the client’s request, and return a message, the client receives the message and parses the output.
Server code
Try {// Create a server socket and open ServerSocketChannel ServerSocketChannel = ServerSocketChannel.open(); / / to monitor serverSocketChannel binding port 8090. The socket (). The bind (new InetSocketAddress (8090)); / / set to non-blocking mode serverSocketChannel. ConfigureBlocking (false);
while(true) {/ / get request connected SocketChannel SocketChannel = serverSocketChannel. The accept ();if(socketChannel! =null){ ByteBuffer buf1 = ByteBuffer.allocate(1024); socketChannel.read(buf1); buf1.flip();if(buf1.hasRemaining())
System.out.println(">>> Server receives data:"+new String(buf1.array())); buf1.clear(); // Construct the returned packet, which is divided into header and body. In actual situations, complex packet protocols can be constructed. ByteBuffer header = ByteBuffer.allocate(6); header.put("[head]".getBytes());
ByteBuffer body = ByteBuffer.allocate(1024);
body.put("i am body!".getBytes());
header.flip();
body.flip();
ByteBuffer[] bufferArray = { header, body };
socketChannel.write(bufferArray);
socketChannel.close();
}else{
Thread.sleep(1000);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Copy the code
Server side selector(selector version)
Try {// open Selector = Selector. Open (); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8090)); serverSocketChannel.configureBlocking(false); / / registered selector with the channel, and registered to accept events serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT);while (trueInt readyChannels = selectNow(); // If not, try againif (readyChannels == 0) continue; // Get the Set of events in the prepared channel Set selectedKeys = selectedKeys(); Iterator keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if(key.isacceptable ()) {// Write the business logic in the event you register, // I register the accept event, // This part of the logic is the same as the non-selector server code above. ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = serverSocketChannel1.accept(); ByteBuffer buf1 = ByteBuffer.allocate(1024); socketChannel.read(buf1); buf1.flip();if (buf1.hasRemaining())
System.out.println(">>> Server receives data:" + new String(buf1.array()));
buf1.clear();
ByteBuffer header = ByteBuffer.allocate(6);
header.put("[head]".getBytes());
ByteBuffer body = ByteBuffer.allocate(1024);
body.put("i am body!".getBytes());
header.flip();
body.flip();
ByteBuffer[] bufferArray = {header, body};
socketChannel.write(bufferArray);
socketChannel.close();
} else if (key.isConnectable()) {
} else if (key.isReadable()) {
} else if(key.iswritable ()) {} // Note the keyiterator.remove () call at the end of each iteration. //Selector does not remove the SelectionKey instance from the selected key set itself. It must be removed by itself when the channel is processed. // The next time the channel becomes ready, the Selector puts it in the selected keysetkeyiterator.remove () again; } } } catch (IOException e) { e.printStackTrace(); }Copy the code
Client code
SocketChannel SocketChannel = socketchannel.open (); SocketChannel = socketchannel.open (); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8090)); // Request server, send request ByteBuffer buf1 = ByteBuffer. Allocate (1024); buf1.put("Request from the client.".getBytes());
buf1.flip();
if(buf1.hasRemaining()) socketChannel.write(buf1); buf1.clear(); We define the first 6 bytes as the header and the following other bytes as the body content. ByteBuffer header = ByteBuffer.allocate(6); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; socketChannel.read(bufferArray); header.flip(); body.flip();if (header.hasRemaining())
System.out.println(">>> Client receives header data:" + new String(header.array()));
if (body.hasRemaining())
System.out.println(">>> Client receives body data:" + new String(body.array()));
header.clear();
body.clear();
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
Copy the code
The results
Server:
Client:
Two versions of the server code are shown here, a non-selector version and a selector version. The final running result shows that the client correctly parses the header and body contents according to the protocol format agreed by both parties. In fact, this is the main function and application scenario of aggregation and dispersion. In network interaction, the format of protocol packets is defined and implemented. After learning the introduction to NIO programming, we finally carried out the summative actual combat, wrote a RPC demo framework to realize the remote call of distributed system. Interested students can pay attention to the author and subsequent articles.
reference
The JAVA NIO
Recommended reading
Java Lock ReentrantLock (part 1)
Java Lock ReentrantLock (part 2)
Java Lock ReentrantReadWriteLock
Introduction to JAVA NIO Programming (PART 1)