NIO realizes Internet chat room

Posted by shoutdots on Mon, 16 Mar 2020 15:21:41 +0100

1. NIO completes network programming

1.1 Selector boss
Selector
	Selector, network programming using NIO's big brother!!!
	The server can execute a thread, run the Selector program, and listen.
	New connection, connected, read data, write data

Common methods of Selector:
	public static Selector Open();
		Get a selector object
	public int select(long timeout);
		Listen to all registered channels and store the corresponding information SelectionKey into the internal set if there is IO flow
		Closing, parameter is a timeout
	public Set<SelectionKey> selectionKeys();
		Returns all selectionkeys saved in the current Selector internal collection

1.2 SelectionKey
SelectionKey
	Indicates the direct relationship between Selector and network channel
	Int OP? Accept; 16 connection required
	Int OP? Connect; 8 connected
	Int OP? Read; 1 read operation
	Int OP write; 4 write operation
SelectionKey
	public abstract Selector selector();
		Get the Selector object associated with it
	public abstract SelectableChannel channel();
		Get the channel associated with it
	public final Object attachment();
		Get the shared data associated with it
	public abstract SelectionKey interestOps(int ops);
		Set or change listening events
	public final boolean isAcceptable();
		Can I accept
	public final boolean isReadable();
		Whether it can be read
	public final boolean isWritable();
		Can I write
##### 1.3 ServerSocketChannel

ServerSocketChannel
Channel channel corresponding to the Socket program of the server
Common methods:
public static ServerSocketChannel open();
Opening the ServerSocketChannel channel channel is equal to starting the server program
public final ServerSocketChannel bind(SocketAddress local);
Set server-side slogans
public final SelectableChannel configureBlocking(boolean block);
Set the blocking or non blocking mode. The value of false indicates that the non blocking mode is adopted
public SocketChannel accept();
[non blocking]
Get a client connection and get the corresponding operation channel
public final SelectionKey register(Selector sel, int ops);
[key methods]
Register the current selector and choose what events to listen to

1.4 SocketChannel
SocketChannel
	Channel object corresponding to client Socket

Common methods:
	public static SocketChannel open();
		Clock in a Socket client Channel object
	public final SelectableChannel configureBlocking(boolean block)
		Here you can set whether it is blocking or non blocking
		false for non blocking
	public boolean connect(SocketAddress remote);
		Connect to server
	public boolean finishConnect();
		If the connect connection fails, you can continue to connect through finishConnect
	public int write(ByteBuffer buf);
		Write data to buffer stream
	public int read(ByteBuffer buf);	,
		Read data from buffer stream
	public final SelectionKey register(Selector sel, int ops, Object attechment);
		Register the current SocketChannel, select the corresponding listening operation, and it can have the Object attachment parameter
	public final void close();
		Close SocketChannel

1.5 using NIO to complete a client and server

1.5.1 first complete the client
package com.qfedu.a_tcpnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Comply with TCP protocol, non blocking IO NIO completes corresponding client code
 *
 * @author Anonymous 2020/3/16 15:10
 */
public class TcpNioClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 1. Get a network channel
        SocketChannel socket = SocketChannel.open();

        // 2. Set the current NIO mode as non blocking mode
        socket.configureBlocking(false);

        // 3. Determine the server IP address and corresponding program port number, and create an InetSocketAddress object
        InetSocketAddress address = new InetSocketAddress("192.168.31.154", 8848);

        // 4. Connect to the server
        if (!socket.connect(address)) {
            // If it is false, it means that the connection fails and the status of the application connection is maintained
            while (!socket.finishConnect()) {
                // Because NIO non blocking mode is adopted, other operations of the current program can be done in the state of obtaining waiting connection.
                System.out.println("Keep calling the server, but I can do something else~~~ Wait 2 s Continue to apply for connection~~~");
                Thread.sleep(2000);
            }
        }

        // 5. Prepare a data buffer
        ByteBuffer buffer = ByteBuffer.wrap("Hello, server, I'm waiting for you...".getBytes());

        // 6. Channel objects are sent through SocketChannel to meet the requirements of the TCP protocol Socket
        socket.write(buffer);

        new Scanner(System.in).nextLine();
    }
}
1.5.2 complete the server
1. Turn on the server 
	ServerScoketChannel
2. open Selector Eldest brother
	Selector object
3. The server ServerSocketChannel bind port number  
	8848 port
4. Set non blocking status
	configureBlocking(false)
5. Selector register--> ServerSocketChannel
	register(selector, OP_ACCEPT);

6. Selector Big brother starts to work
	6.1 Get the connection and register the corresponding Socket
	6.2 Listen to read and write events
		6.2.1 Read the data. Client sends data to server
		6.2.2 Write data. Send data to client
package com.qfedu.a_tcpnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * Use ServerSocketChannel NIO non blocking mode to complete the server code
 *
 * 1. Turn on the server
 * 	    ServerScoketChannel
 * 2. Open Selector
 * 	    Selector object
 * 3. Server ServerSocketChannel bind listening port number
 * 	    8848 port
 * 4. Set non blocking status
 * 	    configureBlocking(false)
 * 5. ServerSocketChannel Register -- > selector
 * 	    register(selector, OP_ACCEPT);
 *
 * 6. Selector Big brother starts to work
 * 	    6.1 Get the connection and register the corresponding Socket
 * 	    6.2 Listen to read and write events
 * 		    6.2.1 Read the data. Client sends data to server
 * 		    6.2.2 Write data. Send data to client
 *
 *
 * @author Anonymous 2020/3/16 15:44
 */
public class TcpNioServer {
    public static void main(String[] args) throws IOException {
        // 1. Turn on the server
        ServerSocketChannel serverSocket = ServerSocketChannel.open();

        // 2. Open the Selector
        Selector selector = Selector.open();

        // 3. Binding port number of server code
        serverSocket.bind(new InetSocketAddress(8848));

        // 4. Set non blocking status
        serverSocket.configureBlocking(false);

        // 5. ServerSocketChannel registration - > the return value of selector is a SelectionKey
        // And make sure that the current Selector listens to selectionkey.op'u accept and listens to the connected server
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);

        // 6. Big brother works
        while (true) {
            // 6.1 get the connection and register the corresponding Socket
            if (0 == selector.select(1000)) {
                // 0 == selector.select(1000) indicates no connection to the client
                System.out.println("ServerSocket Prompt: at present, there is no client to take care of me. I draw circles silently~~~");
                continue;
            }

            // 6.2 listening to read / write events
            // Get all selectionkeys in the current Selector
            Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
            while (selectionKeys.hasNext()) {

                SelectionKey selectionKey = selectionKeys.next();
                // 6.2.1 judge whether the client is a connection request OP? Accept
                if (selectionKey.isAcceptable()) {
                    System.out.println("Client requests connection!!!");
                    // Get the corresponding Socket, but here is to get the corresponding SocketChannel
                    SocketChannel socket = serverSocket.accept();
                    // Set the SocketChannel object of the current corresponding client to a non blocking state
                    socket.configureBlocking(false);
                    /*
                     Register the current Socket object
                     selector Register to the current Selector core
                     SelectionKey.OP_READ Select the operation content monitored by the current Socket to read the data from the current Socket
                     ByteBuffer.allocate(1024 * 4) attachment Supplementary parameter, here is to give the current Socket object a 4KB byte buffer object
                     */
                    socket.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024 * 4));
                }

                // 6.2.2 judge whether the client is in a readable state, and obtain the data sent by the client to the server
                if (selectionKey.isReadable()) {
                    // Get the corresponding SocketChannel object from SelectionKey
                    SocketChannel socket = (SocketChannel) selectionKey.channel();

                    // Because NIO is used, involving Channel and ByteBuffer, the data is in the buffer
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();

                    // Read data. Use SocketChannel to read data from ByteBuffer.
                    socket.read(buffer);

                    System.out.println("Client sends data:" + new String(buffer.array()));
                }

                // After processing, perform a remove current SelectionKey operation
                selectionKeys.remove();
            }
        }
    }
}

2. NIO completes a TCP chat room

2.1 NIO TCP chat client completed
package com.qfedu.b_niochat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * NIO The core code of non blocking TCP chat client
 *
 * @author Anonymous 2020/3/16 16:20
 */
public class ChatClient {
    /**
     * Server IP address
     */
    private static final String HOST = "192.168.31.154";

    /**
     * Port number corresponding to server connection
     */
    private static final int PORT = 8848;

    /**
     * Return NIO request is a scorechannel object
     */
    private SocketChannel socket;

    /**
     * User name
     */
    private String userName;

    /**
     * Client construction method, creating client object
     *
     * @param userName Specified user name
     */
    public ChatClient(String userName) throws IOException, InterruptedException {
        // 1. Open SocketChannel
        socket = SocketChannel.open();

        // 2. Set non blocking status
        socket.configureBlocking(false);

        // 3. Create the corresponding InetSocketAddress according to the specified HOST IP address and the corresponding PORT port number
        InetSocketAddress address = new InetSocketAddress(HOST, PORT);

        // 4. Connect to the server
        if (!socket.connect(address)) {
            // If you are not connected to the server, keep the requested connection
            while (!socket.finishConnect()) {
                System.out.println("Server request connection failed, waiting for 2 s Continue request connection...");
                Thread.sleep(2000);
            }
        }

        this.userName = userName;

        System.out.println("Client " + userName + " Ready");
    }

    /*
    Two methods need to be completed here: one is to send data to the server, and the other is to accept the data sent by the server
     */

    /**
     * Send data to the server for broadcast message and group chat
     *
     * @param message Specified message
     */
    public void sendMsg(String message) throws IOException {
        // Disconnect the server close
        if ("close".equals(message)) {
            socket.close();
            return;
        }
        /*
        StringBuffer
            Thread safety, low efficiency
        StringBuilder
            Unsafe and efficient threads
         */
        message = userName + ":" + message;
        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
        socket.write(buffer);
    }

    public void receiveMsg() throws IOException {
        // Prepare ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        int length = socket.read(buffer);
        if (length > 0) {
            System.out.println(new String(buffer.array()));
        }
    }
}
2.2 completion of NiO TCP chat server

package com.qfedu.b_niochat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**

  • NIO non blocking TCP chat server core code

  • @author Anonymous 2020/3/16 16:59
    */
    public class ChatServer {

    /**

    • Server core module, ServerSocketChannel
      */
      private ServerSocketChannel serverSocket;

    /**

    • NIO Selector selector of server
      */
      private Selector selector;

    /**

    • Specified port number of server listening service
      */
      private static final int PORT = 8848;

    /*

    1. Construction method
    2. Receiving method
    3. Sending method (broadcast)
    4. start the receive and send functions at the same time
      */

    /**

    • Server construction method: open ServerSocketChannel, and at the same time, open Selector to register operation

    • @throws IOException exception
      */
      public ChatServer() throws IOException {
      //1. Start server socket server NiO server
      serverSocket = ServerSocketChannel.open();

      //2. Start selector
      selector = Selector.open();

      //3. Port binding
      serverSocket.bind(new InetSocketAddress(PORT));

      //4. Choose NIO mode as non blocking state
      serverSocket.configureBlocking(false);

      //5. Register SeverSocket and confirm that the current listening status is op ﹣ accept
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      }

    /**

    • Server working method, specify client binding, data receiving and forwarding
      */
      public void start(){
      try {
      while (true) {
      if (0 == selector.select(2000)) {
      System.out.println("the server silently waits for connection, no one accesses..." );
      continue;
      }

           /*
           selectedKeys: 
               Get the corresponding SelectionKey Set collection of all current event operations
            */
           Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
      
           while (iterator.hasNext()) {
               SelectionKey key = iterator.next();
               // 1. connection
               if (key.isAcceptable()) {
                   // Connect the client and get the corresponding SocketChannel object
                   SocketChannel socket = serverSocket.accept();
                   socket.configureBlocking(false);
                   socket.register(selector, SelectionKey.OP_READ);
      
                   // Broadcast on line
                   broadcast(socket, socket.getRemoteAddress().toString() + "Online.");
               }
      
               // 2. Receive data forwarding
               if (key.isReadable()) {
                   readMsg(key);
               }
      
               iterator.remove();
           }
       }
      

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

    /**

    • Read data from the specified SelectionKey

    • @param key the SelectionKey that meets the requirements of op'read
      */
      public void readMsg(SelectionKey key) throws IOException {
      //Get the corresponding SocketChannel object according to the specified SelectionKey
      SocketChannel socket = (SocketChannel) key.channel();

      //Create buffer
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      //Read data from buffer, return value type is the number of bytes read
      int length = socket.read(buffer);

      //Because the read data may have a 0 condition
      if (length > 0) {
      String message = new String(buffer.array());

       // Broadcast data
       broadcast(socket, message);
      

      }
      }

    /**

    • Broadcast method, which is to send messages in groups, but do not send them to yourself

    • @param self client currently sending data

    • @param message message
      /
      public void broadcast(SocketChannel self, String message) throws IOException {
      /
      Get all selectionkeys in the current Selector
      The content registered in the Selector has the SelectionKey corresponding to SocketChannel
      SelectionKey corresponding to ServerSocketChannel
      */
      Set keys = selector.keys();

      //Traverse the entire SelectionKey Set collection
      for (SelectionKey key : keys) {
      //Get the Channel object corresponding to SelectionKey
      SelectableChannel channel = key.channel();

       // First: the channel corresponds to a SocketChannel object, which is not the SocketChannel object of the currently sent message
       if (channel instanceof SocketChannel && !channel.equals(self)) {
           SocketChannel socketChannel = (SocketChannel) channel;
           // Create the corresponding ByteBuffer buffer according to the specified Byte type array
           ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
      
           // send data
           socketChannel.write(buffer);
       }
      

      }
      }

    public static void main(String[] args) throws IOException {
    new ChatServer().start();
    }

}

2.3 NIO TCP chat client thread code implementation
package com.qfedu.b_niochat;

import java.io.IOException;
import java.util.Scanner;

/**
 * Client thread code
 *
 * @author Anonymous 2020/3/16 17:37
 */
public class ChatClientThread {
    public static void main(String[] args) throws IOException, InterruptedException {
        Scanner scanner = new Scanner(System.in);

        System.out.println("enter one user name:");
        String userName = scanner.nextLine();

        if (0 == userName.length()) {
            return;
        }

        ChatClient chatClient = new ChatClient(userName);

        // receive messages
        new Thread(() -> {
            while (true) {
                try {
                    chatClient.receiveMsg();
                    Thread.sleep(2000);
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // send message
        while (scanner.hasNextLine()) {
            String msg = scanner.nextLine();

            chatClient.sendMsg(msg);
        }
    }
}
Published 24 original articles, won praise 21, visited 10000+
Private letter follow

Topics: socket Java network Programming