Summary of error prone knowledge points
-
NIO operates based on channel and buffer. Data is always read from the channel to the buffer or written from the buffer to the channel. The selector is used to listen to the events of multiple channels (such as connection request, data arrival, etc.), so a single thread can listen to multiple client channels
-
Relation diagram of selector, channel and buffer
Relationship description:
1. Each channel corresponds to a buffer
2. A selector corresponds to a thread. A selector can listen to multiple channels (connections)
3. The event determines which channel the program switches to according to the listener of the selector
4. The selector will switch on each channel according to different events monitored
5.buffer is a memory block with an array at the bottom
6. The buffer in NiO can be read or written, but it needs to be switched by flip and other methods
7.channel is bidirectional and can read and write at the same time. It can read and write data asynchronously. It can read data from buffer or write data to buffer -
ServerSocketChannel and SocketChannel are used for TCP data reading and writing.
-
Functions of ServerSocketChannel and SocketChannel: a simple understanding of ServerSocketChannel is that when a client initiates a connection request to the server, the server allocates a SocketChannel to the client through ServerSocketChannel, and then the communication between the client and the server depends on this SocketChannel. Whether it is the client or the server, the channel needs to be connected through the buffer.
Each client has a SocketChannel to communicate with the server Read (buffer), write the contents in the channel into the buffer (read the contents from the channel), channel Write (buffer), write the contents of the buffer to the channel (write the contents to the channel). -
Selector: the selector can detect whether an event occurs on multiple registered channels (Note: multiple channels can be registered to the same selector in the form of events). If an event occurs, it will obtain the event and then process each event accordingly. In this way, only one single thread can manage multiple channels, that is, manage multiple connections and requests.
-
OP_ The read event is triggered not only when it is readable, but also when the data in the channel is read, the other end of the connection pipe is closed, there is an error pending, and the other party sends a message.
Case 1:
Simple server-side and client-side connection (console display)
Server side code:
package com.haust.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Set; public class server { public static void main(String[] args) throws IOException { //Create ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //Get a selector object Selector selector = Selector.open(); //Bind a local 6666 port and listen on the server serverSocketChannel.socket().bind(new InetSocketAddress(6666)); //Set channel to non blocking serverSocketChannel.configureBlocking(false); //Register serverSocketChannel with the selector, and the monitored event is OP_ACCEPT serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true){ //Here, we wait for 1 second. If no event occurs, we return if (selector.select(1000) == 0){//No event occurred within 1 second System.out.println("The server waits for 1 second and there is no connection"); continue; } //If the returned is > 0, the relevant selectionKey collection is obtained //selector.keys() indicates the number of channel s registered on the selector //1. If the returned > 0, it indicates that the event of interest has been obtained //2.selector.selectedKeys() returns the collection of events listened to //Reverse channel acquisition through selectionKeys Set<SelectionKey> selectionKeys = selector.selectedKeys(); //Iterate over the selectionset, where we use the < selectionset > Iterator<SelectionKey> keyIterator = selectionKeys.iterator(); while (keyIterator.hasNext()){ //Get Selectionkey SelectionKey key = keyIterator.next(); //Handle the events according to the channel corresponding to the key if (key.isAcceptable()){//The accept event indicates a new client connection //Generate a socketChannel for the client SocketChannel socketChannel = serverSocketChannel.accept(); //Set to non blocking socketChannel.configureBlocking(false); //Register the socketChannel with the selector, and the listening event is OP_READ, and associate a buffer to the socketChannel at the same time socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024)); } if (key.isReadable()){ //Get the channel in reverse through the key SocketChannel channel = (SocketChannel)key.channel(); //Get the buffer associated with the channel ByteBuffer buffer = (ByteBuffer)key.attachment(); //Read data channel.read(buffer); System.out.println("from client"+new String(buffer.array())); } //Manually move the current selectionkey from the collection to prevent repeated operations keyIterator.remove(); } } } }
Client code
package com.haust.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class client { public static void main(String[] args) throws IOException { //Get a network channel SocketChannel socketChannel = SocketChannel.open(); //Set non blocking socketChannel.configureBlocking(false); //Provide server-side IP and port InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666); //Connect server if (!socketChannel.connect(inetSocketAddress)){ while (!socketChannel.finishConnect()){ System.out.println("Because the connection takes time, the client will not block and can do other work.."); } } //If the connection is successful, send the data String str = "hello nio"; ByteBuffer buffer = ByteBuffer.wrap(str.getBytes()); //Send data and write buffer data to channel socketChannel.write(buffer); System.in.read(); } }
effect:
Case 2: group chat system
Simple server-side and client-side group chat system (console board)
Server side code:
package com.haust.groupchat; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; public class GroupChatServer { //Define attributes private Selector selector; private ServerSocketChannel listenChannel; private static final int PORT =6667; //Constructor initialization public GroupChatServer(){ try { //Get selector selector = Selector.open(); //ServerSocketChannel listenChannel = ServerSocketChannel.open(); //Binding port listenChannel.socket().bind(new InetSocketAddress(PORT)); //Set non blocking mode listenChannel.configureBlocking(false); //Register the listenChannel with the selector listenChannel.register(selector, SelectionKey.OP_ACCEPT); }catch (IOException e) { e.printStackTrace(); } } public void listen(){ try { while (true){ int count = selector.select(2000);//The select method itself is blocked (without parameters). 2000 represents the result returned after 2 seconds and becomes non blocking if (count>0){//Indicates that an event has occurred //Traversal to get selectionKey set Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ //Remove the selectionkey SelectionKey key = iterator.next(); //Listen to accept if (key.isAcceptable()){ SocketChannel sc = listenChannel.accept(); //Register the sc with the selector sc.configureBlocking(false); sc.register(selector,SelectionKey.OP_READ); //Tips System.out.println(sc.getRemoteAddress()+"go online"); } if (key.isReadable()){ readData(key); } //Prevent repeated operations iterator.remove(); } }else { // System.out.println("wait...); } } }catch (IOException e) { e.printStackTrace(); }finally { //Handling in case of exception } } //Read client messages public void readData(SelectionKey key){ SocketChannel channel = null; try { //Get channel channel = (SocketChannel) key.channel(); //Create buffer ByteBuffer buffer = ByteBuffer.allocate(1024); int count = channel.read(buffer); //Perform corresponding processing according to the value of count if (count>0){ //Convert the data in the cache into a string String msg = new String(buffer.array()); //Output this message System.out.println("from client"+msg); //Forward messages to other clients (remove yourself) sendInfoToOtherClients(msg,channel); } }catch (IOException e){ try { System.out.println(channel.getRemoteAddress()+"Offline"); //Unregister key.cancel(); //Close channel channel.close(); } catch (IOException ex) { ex.printStackTrace(); } } } //Method of forwarding message to other clients private void sendInfoToOtherClients(String msg,SocketChannel self) throws IOException { System.out.println("Server forwarding message"); //Traverse all socketchannels registered on the selector and exclude self for (SelectionKey key : selector.keys()) { //Get the corresponding SocketChannel through the key Channel targetChannel = key.channel(); //Exclude yourself if (targetChannel instanceof SocketChannel && targetChannel != self){ //transformation SocketChannel dest = (SocketChannel)targetChannel; //Store msg in buffer ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes()); //Write buffer data to channel dest.write(buffer); } } } public static void main(String[] args) { //Create server object GroupChatServer groupChatServer = new GroupChatServer(); groupChatServer.listen(); } }
Client code
package com.haust.groupchat; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; 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 GroupChatClient { //Define related attributes private final String HOST = "127.0.0.1";//IP address of the server private final int PORT = 6667;//Server port private Selector selector; private SocketChannel socketChannel; private String username; //Constructor to complete initialization public GroupChatClient() throws IOException { selector = Selector.open(); //Connect server socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1",PORT )); //Set non blocking socketChannel.configureBlocking(false); //Register channel with selector socketChannel.register(selector, SelectionKey.OP_READ); //Get username username = socketChannel.getLocalAddress().toString().substring(1); System.out.println(username+"is OK"); } //Send message to server public void sendInfo(String info){ info = username + " say:" +info; try { socketChannel.write(ByteBuffer.wrap(info.getBytes())); } catch (IOException e) { e.printStackTrace(); } } public void readInfo(){ try { int readChannels = selector.select(2000); if (readChannels > 0){//Indicates that there are channels available Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ SelectionKey key = iterator.next(); if (key.isReadable()){ //Get the relevant channel SocketChannel sc = (SocketChannel)key.channel(); //Get a buffer ByteBuffer buffer = ByteBuffer.allocate(1024); //read sc.read(buffer); //Convert the data of the read buffer into a string String msg = new String(buffer.array()); System.out.println(msg.trim()); //Delete the current selectionkey to prevent repeated operations iterator.remove(); } else{ // System.out.println("no channel temporarily"); } } } }catch (IOException e){ e.printStackTrace(); } } public static void main(String[] args) throws IOException { //Start client final GroupChatClient chatClient = new GroupChatClient(); //Start a thread to read the data sent from the server every three seconds new Thread(){ public void run(){ while (true){ chatClient.readInfo(); try{ Thread.currentThread().sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); //Send data to server Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()){ String s = scanner.nextLine(); chatClient.sendInfo(s); } } }
Special note: the channel registered with the selector is non blocking! After the listening event is processed, the key corresponding to this listening event needs to be removed to prevent repeated execution. (if not removed, it will be in the collection next time)
If the same program is run multiple times (different threads are applied), it needs to be set in the idea
effect:
Disconnect the three clients