preface
In the previous chapter, we understood the three cores of Java NIO, and focused on the principle of Buffer and several usage scenarios, in which channel is also used. In this chapter, let's understand selector and combine channel to do c/s communication.
Understanding Selector and Channel
The Selector, also known as the multiplexer, can handle multiple client connections at the same time. The multiplexer uses the polling mechanism to select the client links with read-write events for processing.
Through the Selector, an I/O thread can handle N client connections and read-write operations concurrently, which solves the traditional synchronous blocking I/O connection thread model, and greatly improves the performance, elasticity, scalability and reliability of the architecture.
Because its read and write operations are non blocking, it can fully improve the operation efficiency of IO threads and avoid thread suspension caused by frequent I/O blocking.
Here, I'm sorting out its working principle, as shown in the figure:
Selector: the function of multiplexer (also called selector) is to provide to SocketChannel channel for registration, and then the selector will poll to listen for the read / write events of the channel, so as to make corresponding IO processing. Its workflow is as follows:
-
First, you need to create a ServerSocketChannel, which is similar to ServerSocket. You need to specify a listening IP and Port. It should be noted that ServerSocketChannel is blocked by default in order to be compatible with BIO, and can be specified as NIO mode through ServerSocketChannel#configureBlocking(false).
A client's SocketChannel can be obtained through ServerSocketChannel. The client's SocketChannel needs to be registered with the Selector, and then each channel will correspond to a SelectionKey
-
Selectors can be accessed through the Selector Open() and register ServerSocketChannel with the Selector. The Selector#select() method of the Selector can select the channel with events (SocketChannel) and return the number of ready channels Event types include: "connect", "receive", "read", "write".
-
If the return value of Selector#select() is greater than 0, it means that some channels have events. You can use selector Selectedkeys() to get all selectionkeys with event channels.
Then you can get the SocketChannel through the SelectionKey direction, so as to read the data in the SocketChannel into the Buffer and complete the IO operation.
ServerSocketChannel
ServerSocketChannel is used by the server to listen to the client Socket link. You can obtain the client SocketChannel through the accept method, so as to register the SocketChannel with the Selector. The relevant methods are as follows
- open: create a ServerSocketChannel channel
- bind(SocketAddress local): set the address and port number that the server listens to
- configureBlocking(boolean block): sets the blocking or non blocking mode. The value of false indicates that the non blocking mode is adopted
- accept(): accept a connection and return the channel object SocketChannel representing the connection
- register(Selector sel, int ops): register the current channel with the selector and set listening events
SocketChannel
When a client links to the server, a channel will be generated: ServerChannel, which needs to be registered with the Selector, and the Selector will listen for the read and write events of the channel. ServerChannel is responsible for specific reading and writing, writing the data in the buffer to the channel, or writing the data in the channel to the buffer.
- open(): get a SocketChannel channel
- configureBlocking(boolean block): sets the blocking or non blocking mode. The value of false indicates that the non blocking mode is adopted
- connect(SocketAddress remote): connect to the remote server and specify the IP and port through SocketAddress
- finishConnect(): if the connect connection fails, the connection operation must be completed through the finishConnect method
- write(ByteBuffer src): write the data in ByteBuffer to the channel
- read(ByteBuffer dst): read data from the channel and write to ByteBuffer
- register(Selector sel, int ops, Object att): register the channel with the selector and set listening events (OPS). The last parameter can set shared data. This method will return a selection key corresponding to the channel.
- close(): close the channel
SelectionKey
When the ServerChannel is registered with the Selector, a SelectionKey will be generated. Through the SelectionKey, the SocketChannel channel object can be obtained in reverse, so as to carry out IO reading and writing operations.
- selector(): get the associated selector object through SelectionKey
- channel(): get the associated channel
- attachment(): get the shared data associated with it
- interestOps(int ops): set or change listening events
- isAcceptable(): whether the "receive ready" event can be accepted
- isReadable(): can I read
- isWritable(): can I write
Events include:
SelectionKey.OP_CONNECT : Value 16, connection ready, for example: one channel Connect to a server SelectionKey.OP_ACCEPT : Value 8, receive ready, for example: ServerSocketChannel Ready to connect to a new connection SelectionKey.OP_READ : Value 1, "read ready", the channel has data that can be read, which can be said to be SelectionKey.OP_WRITE : Value 4, "write ready", the channel waiting to write data can be said
Selector
It is responsible for monitoring the channel by polling. When the channel has read-write events, it will carry out IO operations.
- open(): get a selector object
- select(long timeout): select the channel with events, add its corresponding SelectionKey to the internal collection, and return the number of channels
- selectedKeys(): get all event selectionkeys from the internal collection
- keys(): get all selectionkeys from the internal collection
- Close: close the selector
Comprehensive case
Use selector, ServerSocketChannel and SocketChannel to complete a C/S case,
Server code
//passageway @Test public void serverSocketChannelTest() throws IOException { //Create server channel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //socket listening address and port SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",5000); //Bind to a SocketAddress serverSocketChannel.bind(socketAddress); //NIO adopts blocking by default. In order to be compatible with BIO serverSocketChannel.configureBlocking(false); //Create selector Selector selector = Selector.open(); //The channel is registered to the selector, and the event type is: OP_ACCEPT "accept" serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //==Selector polling======================================================================= while(true){ //Select, select the channel with events, and return the number of key s of the channel with events. The timeout is 1s if(selector.select(1000) == 0){ System.out.println("No connection...Polling wait...\"); continue; } //When an event occurs, get the set of key s of the channel with the event Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); //Traverse the collection of key s while (iterator.hasNext()){ //Get the key of each channel SelectionKey key = iterator.next(); //If the current channel event is: OP_ACCEPT, register the channel if(key.isAcceptable()){ //Receive a socketChannel SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("Client link succeeded..."); socketChannel.configureBlocking(false); //Register the socketChannel with the selector and bind a buffer to the channel socketChannel.register(selector, SelectionKey.OP_READ,ByteBuffer.allocate(1024)); } //If the channel event is: OP_READ indicates that the channel has data if(key.isReadable()){ //Get SocketChannel through key SocketChannel channel = (SocketChannel)key.channel(); //Get the channel bound buffer ByteBuffer byteBuffer = (ByteBuffer)key.attachment(); //Read data from channel to buffer channel.read(byteBuffer); System.out.println(new String(byteBuffer.array())); } //Delete current key iterator.remove(); } } }
Client code
//passageway @Test public void socketChannelTest() throws IOException { //Create a SocketChannel SocketChannel socketChannel = SocketChannel.open(); //Use non blocking mode socketChannel.configureBlocking(false); //Linked address and port InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 5000); //Try to link. If you use asynchronous, you need to use socketchannel Finishconnect() to ensure a successful connection. if(!socketChannel.connect(inetSocketAddress)){ //If the link is not successful, it will go through the while loop until the finishConnect link is successful and jump out of the while loop while(!socketChannel.finishConnect()){ System.out.println("The link has not been completed...Waiting..."); } } //The link is successful. Write out the data socketChannel.write(ByteBuffer.wrap("Hello".getBytes())); System.out.println("Send data to the server..."); //Prevent the client from ending, so use read() to block System.in.read(); }
summary
This article introduces the selector, serversocketchannel, the role of SocketChannel, scenario API, and the working principle of the three, and demonstrates the relationship between the three through a C/S comprehensive case.
At the end of the article, I hope it will help you. Your encouragement is my biggest motivation. If you like it, please don't be stingy with your one button three times!!!