“This is the first day of my participation in the November Gwen Challenge. Check out the details: The Last Gwen Challenge 2021


Netty is a network communication framework encapsulated in Java NIO. Only a thorough understanding of Java NIO can understand the underlying design of Netty. Java NIO consists of three core components:

  • Buffer
  • Channel
  • Selector

Buffer

A Buffer is a data object. It can be thought of as a container of a fixed amount of data that is to be written or read.

In Java NIO, any time you access data in the NIO, you need to operate through a Buffer. When reading data, it is read directly from the buffer. When writing data, it is written to the buffer. The most common NIO buffer is ByteBuffer. The following is the Buffer inheritance diagram:

Each Java primitive type has a Buffer that contains the same operation, but the data type is different.

Channel is the Channel

A Channel is a Channel through which network data is read and written just like a water pipe. Traditional IO operates on streams, Channle is similar but a little different:

The difference between flow Through the Channel
Support asynchronous Does not support support
Whether data can be transmitted in both directions No, only one way Yes, data can be read from or written to a channel
Whether to use it together with Buffer Don’t Must be used in conjunction with Buffer
performance The lower higher

As mentioned above, a Channel must be used in conjunction with a Buffer. You can never write data directly to a Channel, nor can you read data directly from a Channel. Both read data from a Channel to Buffer or write data from a Buffer to a Channel, as follows:

To put it simply, a Channel is a source or destination of data, used to feed or read buffer data, and provides asynchronous SUPPORT for I/O.

Below is a class diagram for a Channel

Channel is the top-level interface. All sub-channels implement this interface. It is mainly used for I/O connection. The definition is as follows:

Public Interface Channel extends Closeable {/** * Checks whether the Channel is open. */ public boolean isOpen(); /** * Close this channel. */ public void close() throws IOException; }Copy the code

The most important Channel implementation class is:

  • FileChannel: A channel for writing, reading, mapping, and manipulating files
  • DatagramChannel: Read and write network data through UDP
  • SocketChannel: Reads and writes data on the network through TCP
  • ServerSocketChannel: Can listen for incoming TCP connections, just like a Web server. A SocketChannel is created for each incoming connection

A multiplexer Selector

The Selector multiplexer, which is the foundation of Java NIO programming, provides the ability to select tasks that are already in place. At the bottom, Selector provides the ability to ask a channel if it is ready to perform each I/O operation. Basically, a Selector constantly polls a Channel registered on it, and if a read or write event occurs on a Channel, that Channel is ready to be polled by the Selector, The SelectionKey is then used to retrieve a collection of ready channels for subsequent I/O operations.

Selector allows a single thread to handle multiple channels, which means that a single thread can handle thousands of channels with a complex Selector poll, which will inevitably reduce context switching problems for threads compared to multi-threading. Here is a Selector connecting three channels:

The instance

The service side

Public class NIOServer {/* Accept data buffer */ private ByteBuffer sendBuffer = ByteBuffer. Allocate (1024); Private ByteBuffer ReceiveBuffer = ByteBuffer. Allocate (1024); private Selector selector; Public NIOServer(int port) throws IOException {// open the ServerSocketChannel ServerSocketChannel ServerSocketChannel = ServerSocketChannel.open(); / / the server configuration for a non-blocking serverSocketChannel. ConfigureBlocking (false); / / retrieves a server socket associated with this channel ServerSocket ServerSocket = serverSocketChannel. The socket (); Serversocket. bind(new InetSocketAddress(port)); Selector = Selector. Open (); / / registered to the selector, wait for connection serverSocketChannel. Register (selector, SelectionKey. OP_ACCEPT); System.out.println("Server Start----:"); } private void listen() throws IOException { while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); handleKey(selectionKey); }} private void handleKey(SelectionKey SelectionKey) throws IOException {// Accept request ServerSocketChannel Server = null; SocketChannel client = null; String receiveText; String sendText; int count=0; // Tests whether the channel for this key is ready to accept a new socket connection. If (selectionKey isAcceptable ()) {/ / return to create this key channels. server = (ServerSocketChannel) selectionKey.channel(); // Accept a connection to this channel socket. // The socket channel returned by this method (if any) will be in blocking mode. client = server.accept(); // Configure client.configureblocking (false); Register (selector, selectionkey.op_read); } else if (selectionKey.isreadable ()) {// Returns the channel for which this key was created. client = (SocketChannel) selectionKey.channel(); Receivebuffer.clear (); // Clear the buffer for the next read. // Read data from the server into the buffer count = client.read(receivebuffer); if (count > 0) { receiveText = new String( receivebuffer.array(),0,count); System.out.println(" Server receives client data --:"+receiveText); client.register(selector, SelectionKey.OP_WRITE); }} else if (selectionkey.iswritable ()) {// Clear the buffer for the next write sendBuffer.clear (); // Returns the channel for which this key was created. client = (SocketChannel) selectionKey.channel(); sendText="message from server--"; Sendbuffer.put (sendText.getBytes())); // Reset sendBuffer.flip (); // Reset sendbuffer.flip(); // Reset sendbuffer.flip(); // Reset sendbuffer.flip(); // Output to channel client.write(sendbuffer); System.out.println(" server sends data to client -- : "+sendText); client.register(selector, SelectionKey.OP_READ); } } public static void main(String[] args) throws IOException { int port = 8080; NIOServer server = new NIOServer(port); server.listen(); }}Copy the code

The client

Public class NIOClient {/* Accept data buffer */ private static ByteBuffer sendBuffer = ByteBuffer. Allocate (1024); Private static ByteBuffer ReceiveBuffer = ByteBuffer. Allocate (1024); Public static void main(String[] args) throws IOException {// Open the socket channel SocketChannel = SocketChannel.open(); / / set to non-blocking way socketChannel. ConfigureBlocking (false); Selector = Selector. Open (); Socketchannel. register(selector, selectionkey.op_connect); // Connect to socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080)); Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey; SocketChannel client; String receiveText; String sendText; int count=0; While (true) {// Select a set of keys whose corresponding channels are ready for I/O operations. // This method performs the selection operation in blocking mode. selector.select(); // Returns the selected keyset for this selector. selectionKeys = selector.selectedKeys(); //System.out.println(selectionKeys.size()); iterator = selectionKeys.iterator(); while (iterator.hasNext()) { selectionKey = iterator.next(); if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // Check whether a connection operation is in progress on this channel. // Complete the socket channel connection. if (client.isConnectionPending()) { client.finishConnect(); System.out.println(" Complete connection!" ); sendbuffer.clear(); sendbuffer.put("Hello,Server".getBytes()); sendbuffer.flip(); client.write(sendbuffer); } client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { client = (SocketChannel) selectionKey.channel(); Receivebuffer.clear (); // Clear the buffer for the next read. // Read data from the server into the buffer count=client.read(receivebuffer); if(count>0){ receiveText = new String( receivebuffer.array(),0,count); System.out.println(" client receives server data --:"+receiveText); client.register(selector, SelectionKey.OP_WRITE); } } else if (selectionKey.isWritable()) { sendbuffer.clear(); client = (SocketChannel) selectionKey.channel(); sendText = "message from client--"; sendbuffer.put(sendText.getBytes()); // Reset sendBuffer.flip (); // Reset sendbuffer.flip(); // Reset sendbuffer.flip(); // Reset sendbuffer.flip(); client.write(sendbuffer); System.out.println(" client sends data to server -- : "+sendText); client.register(selector, SelectionKey.OP_READ); } } selectionKeys.clear(); }}}Copy the code

The results