[consolidate the foundation of java] network programming, I read it and said it was good

Posted by josemi on Thu, 25 Nov 2021 01:31:03 +0100


Three elements of network programming

  • Protocol: TCP/UDP

  • IP address

  • Port number

TCP

TCP: transmission control protocol. TCP protocol is a connection oriented communication protocol, that is, before transmitting data, establish a logical connection between the sender and the receiver, and then transmit data. It provides reliable error free data transmission between two computers.

TCP protocol features: connection oriented, secure data transmission and low transmission speed

Three handshakes

• for the first handshake, the client sends a connection request to the server and waits for the server to confirm. What are you worried about?

• for the second handshake, the server sends back a response to the client to notify the client that it has received the connection request. What do I worry about you?

• for the third handshake, the client sends a confirmation message to the server again to confirm the connection. The whole interaction process is shown in the figure below. Try again

After three handshakes are completed and the connection is established, the client and server can start data transmission. Because of this connection oriented feature, TCP protocol can ensure the security of data transmission, so it is widely used, such as downloading files, browsing web pages and so on.

Four handshakes of TCP in socket to release connection

Four waves: in TCP protocol, four waves are required to release the connection after sending data.

  • The first wave: the client puts forward to the server to end the connection and let the server make the final preparations. At this time, the client is in a semi closed state, which means that it no longer sends data to the server, but it can still accept data.

  • Second wave: after receiving the request from the client to release the connection, the server will send the last data to the client. And inform the upper application process that it will no longer receive data.

  • The third wave: after the server sends the data, it will send a message to release the connection to the client. Then the client will know that the connection can be officially released after receiving it.

  • The fourth wave: after receiving the last release connection message from the server, the client shall reply to a completely disconnected message. In this way, the connection will not be completely released until the server receives it.

UDP

UDP: user datagram protocol. UDP protocol is a connectionless protocol. When transmitting data, there is no need to establish a connection. No matter whether the other side service is started or not, the data, data source and destination are directly encapsulated in data packets and sent directly. The size of each packet is limited to 64k. It is an unreliable protocol, because there is no connection, so the transmission speed is fast, but it is easy to lose data. In daily applications, such as shipin meeting, QQ chat, etc.

  • UDP features: no connection oriented, insecure data transmission, fast transmission speed

  • For example, the village head found that Zhang San's cattle had been lost

  • UDP protocol: the village head broadcasts at the village radio station that the cattle of Zhang San's family are lost, the information is lost, and the information release speed is fast

IP

  • To view the local IP address, enter:

ipconfig

  • Check whether the network is connected. Enter:

ping IP address
ping 220.181.57.216
ping www.baidu.com

Summary

  • Protocol: the rules that computers need to follow when communicating in the network. The common protocols are TCP and UDP

  • TCP: connection oriented, secure data transmission, slow transmission speed

  • UDP: for connectionless, data transmission is not safe, and the transmission speed is fast

  • IP address: used to identify computer equipment in the network

    • Classification: IPV4 IPV6
    • Local ip address: 127.0.0.1 localhost
  • Port number: used to identify the application program in the computer device, 0 – 65535

    • The port number specified by the program written by yourself should be more than 1024
    • If the port number is occupied by another service or application, the current program will fail to start.

TCP communication program

// client
public class Client {
    public static void main(String[] args) throws Exception {
        // Client connection server
        Socket socket = new Socket("127.0.0.1", 6666);
        System.out.println("I am a client:" + socket.getLocalPort() + " Start successful");

        while (true) {
            // The client sends data to the server
            OutputStream out = socket.getOutputStream();
            Scanner scanner = new Scanner(System.in);
            String info = scanner.next();
            out.write(("client:"+info).getBytes());
            out.flush();

            // Client accepts server data
            InputStream in = socket.getInputStream();
            byte[] bytes = new byte[8721];
            int len = in.read(bytes);
            String s = new String(bytes, 0, len);
            System.out.println(s);
        }

        // socket.close();
    }
}
// Server
public class Server {
    public static void main(String[] args) throws Exception {
        // Server open port
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("I am the server:" + serverSocket.getLocalPort() + " Start successful");
        // Wait for the client to connect. It will be blocked before connecting
        Socket accept = serverSocket.accept();
        System.out.println("I'm the server. Hello, client:" + accept.getPort() + ",Welcome to connect~");

        while (true) {
            // The server accepts client data
            InputStream in = accept.getInputStream();
            byte[] bytes = new byte[8721];
            int len = in.read(bytes);
            String s = new String(bytes, 0, len);
            System.out.println(s);

            // The server sends data to the client
            OutputStream out = accept.getOutputStream();
            Scanner scanner = new Scanner(System.in);
            String info = scanner.next();
            out.write(("Server:"+info).getBytes());
            out.flush();
        }

        // accept.close();
        // serverSocket.close();

    }
}

TCP communication file operation

public class Client {
    public static void main(String[] args) throws Exception{
       // client:
       // 1. Create a Socket object and specify the ip address and port number of the server to be connected
        Socket socket = new Socket("127.0.0.1",6666);

       // 2. Create byte input stream object and associate data source file path
        FileInputStream fis = new FileInputStream("d:\\img\\a.jpg");

       // 3. Obtain the output stream through the Socket
        OutputStream os = socket.getOutputStream();

        // 4. Define a byte array to store the read byte data
        byte[] bys = new byte[8192];

       // 4. Define an int variable to store the number of bytes read
        int len;

       // 5. Read data circularly
        while ((len = fis.read(bys)) != -1) {
            // 6. In the loop, write out the data
            os.write(bys, 0, len);
        }

        // os.write("");
        // -When the file is uploaded, if the client cannot read the data from the file, it will stop sending.
        // However, the server does not know that the client has stopped writing data, so it will always wait to receive the data written by the client.
        // Solution: call s.shutdownOutput() on the client; Notify the server that sending is over.
        socket.shutdownOutput();

        System.out.println("=======Start receiving data written back by the server=======");
        //7. Obtain the input stream through the Socket
        InputStream is = socket.getInputStream();

        //8. Read the string data written back by the server
        int read = is.read(bys);// Stuck
        System.out.println(new String(bys,0,read));

        // 9. Close the flow and release resources
        fis.close();
        socket.close();

    }

public class Server {
    public static void main(String[] args) throws Exception{
        // The server:
        // 1. Create a ServerSocket object and specify the port number (6666)
        ServerSocket ss = new ServerSocket(6666);

        // Cyclic receive request
        while (true){
            // 2. Call the accept() method to receive the client request and get the Socket object
            final Socket socket = ss.accept();
            System.out.println("New client: " + socket.getPort());
            // As long as the connection is established, a thread is opened to upload files
            new Thread(new Runnable() {

                public void run() {
                    try{
                        // 3. Obtain the input stream through the returned Socket object
                        InputStream is = socket.getInputStream();

                        // 4. Create byte output stream object and associate destination file path
                        FileOutputStream fos = new FileOutputStream("d:\\img\\"+System.currentTimeMillis()+".jpg");

                        // 5. Define a byte array to store the read byte data
                        byte[] bys = new byte[8192];

                        // 5. Define an int variable to store the number of bytes read
                        int len;

                        Thread.sleep(10000);
                        // 6. Read data circularly
                        while ((len = is.read(bys)) != -1) {// Stuck
                            // 7. In the loop, write out the data
                            fos.write(bys, 0, len);
                        }

                        System.out.println("============Start writing back data to the client==========");
                        // 8. Obtain the output stream through the returned Socket object
                        OutputStream os = socket.getOutputStream();

                        // 9. Write out string data to the client
                        os.write("File uploaded successfully!".getBytes());

                        // 10. Close the flow and release resources
                        fos.close();
                        socket.close();
                        //ss.close();
                    }catch (Exception e){

                    }
                }
            }).start();

        }
    }
}

TCP emulation web server

  • Accessing resources in a local project from a browser (similar to Tomcat)
// Server
public class Server {
    public static void main(String[] args) throws Exception {
        // The server:
        // 1. Create a ServerSocket object and specify the port number (6666)
        ServerSocket ss = new ServerSocket(6666);

        while (true) {
            // 2. Call the accept() method to get the request and get the Socket object
            final Socket socket = ss.accept();
            // Connection establishment, thread development
            new Thread(new Runnable() {

                public void run() {
                    try {
                        // 3. Obtain the input stream through the returned Socket object
                        InputStream is = socket.getInputStream();

                        // 4. Through the input stream, go to the connection channel to obtain data, filter, and filter out the page path that the browser needs to access
                        // 4.1 convert byte input stream to character input stream
                        InputStreamReader isr = new InputStreamReader(is);
                        //  4.2 read the data in the first row. Because we need to read the url request, we use BufferedReader to read the data line by line
                        BufferedReader br = new BufferedReader(isr);
                        String line = br.readLine();
                    	// When we request a url, the request received by the server is: request method (GET) + path of our request + HTTP version
                        System.out.println(line);// GET /socketnio/web/index.html HTTP/1.1
                        // 4.3 intercept the page path that the browser needs to visit, that is, we need to visit the index page under the web under socketnio
                        // Separate into an array according to the space, then take the element with index 1, and intercept it from the position with index 1 of this element
                        String path = line.split(" ")[1].substring(1);
                        System.out.println(path);// socketnio/web/index.html
                        // After getting our path, it's equivalent to file transfer
                        // 5. Create a byte input stream object and associate the page path to be accessed
                        FileInputStream fis = new FileInputStream(path);
                        // 6. Obtain the output stream through the Socket object
                        OutputStream os = socket.getOutputStream();
                        // 7. Define a byte array to store the read byte data
                        byte[] bys = new byte[8192];
                        // 7. Define an int variable to store the number of bytes read
                        int len;
                        // When responding to the page, you need to send the following responses to the browser at the same time
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        os.write("\r\n".getBytes());
                        // 8. Read data circularly
                        while ((len = fis.read(bys)) != -1) {
                            // 9. Write data in the loop
                            os.write(bys, 0, len);
                        }
                        // 10. Close the flow and release resources
                        fis.close();
                        socket.close();
                        //ss.close();
                    } catch (Exception e) {
                    }
                }
            }).start();
        }
    }
}

NIO

Synchronous asynchronous, blocking and non blocking

Before learning the NIO flow of Java, first understand several keywords

  • Synchronization and asynchronism (synchronous/asynchronous): synchronization is a reliable and orderly operation mechanism. When we perform synchronous operations, the next task is to wait for the current call to return, and then proceed to the next step. Asynchronous, on the contrary, other tasks do not need to wait for the current call to return, usually rely on events, callbacks and other mechanisms to achieve order relations between tasks.

    • Synchronization: after calling the method, you must get a return value. For example, if you buy a train ticket, you must buy a ticket before you can continue to the next step
    • Asynchronous: after calling the method, there will be no return value, but there will be a callback function. The callback function refers to the method that will be executed automatically after meeting the conditions. For example, if you buy a train ticket, you don't have to buy a ticket. I can tell the conductor that when there is a ticket, you can help me issue a ticket
  • Blocking and non blocking: during blocking operation, the current thread will be in blocking state and cannot engage in other tasks. It can continue only when the conditions are ready, such as the establishment of a new connection to ServerSocket, or the completion of data reading and writing operations; Non blocking is to return directly regardless of whether the IO operation ends or not, and the corresponding operation continues to be processed in the background

    • Blocking: if the purpose of the method is not achieved, it will always stop there (wait). For example, the accept method of ServerSocket
    • Non blocking: the method is executed directly (without waiting) regardless of whether it has achieved its goal or not

NIO is synchronous because the kernel I/O operations of its accept/read/write method will block the current thread

Three components

NIO has three main components: Buffer, Channel and Selector. NIO is only used when the number of accesses is very large. For example, there will be high concurrency and a large number of connections in popular software or popular games

Buffer class

buffer

Three creation methods

// Method 1: create buffer in heap: indirect buffer -------------- > recommended
ByteBuffer b1 = ByteBuffer.allocate(10);

// Method 2: create a buffer in the system memory: direct buffer
ByteBuffer b2 = ByteBuffer.allocateDirect(10);

// Method 3: create a buffer in the heap
byte[] bys = new byte[10];
ByteBuffer b3 = ByteBuffer.wrap(bys);
// Reading the source code, you can see that allocate is the HeapByteBuffer called, so it is a heap memory creation
public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw createCapacityException(capacity);
    return new HeapByteBuffer(capacity, capacity, null);
}
// The wrap method is actually called HeapByteBuffer
public static ByteBuffer wrap(byte[] array,
                              int offset, int length)
{
    try {
        return new HeapByteBuffer(array, offset, length, null);
    } catch (IllegalArgumentException x) {
        throw new IndexOutOfBoundsException();
    }
}
  • And a byte [] array is encapsulated inside the ByteBuffer class

  • The creation and destruction efficiency of indirect buffer is higher than that of direct buffer
  • Indirect buffers are less efficient than direct buffers
Main methods

put,capacity,limit,position,mark,reset,clear,flip

  • Mainly understand clear and flip
package com.liu;

import java.nio.ByteBuffer;
import java.util.Arrays;

public class Demo01 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        // buffer capacity= 10
        System.out.println("buffer of capacity= " + buffer.capacity());

        buffer.put((byte) 1);
        buffer.put((byte) 2);
        // buffer= [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer= " + Arrays.toString(buffer.array()));

        // As the name implies, the buffer is limited to n(3) and N < = capacity
        buffer.limit(3);
        buffer.put((byte) 3);
        // buffer.put((byte)4);  Exception: java.nio.bufferoverflow exception
        // buffer limit(3)= [1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer stay limit(3)= " + Arrays.toString(buffer.array()));

        // Reposition the index position (equivalent to a pointer operating the array index, and position is to change the pointer position). The next operation will start from index n(1), and N < = n in limit
        buffer.position(1);
        buffer.put((byte) 4);
        buffer.put((byte) 5);
        // buffer position(1)= [1, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer stay position(1)= " + Arrays.toString(buffer.array()));

        // mark and reset are together
        // mark marks the position (that is, the position of the array index pointer)
        // reset repositions the pointer to the position marked by mark, that is, 1
        buffer.position(1);
        buffer.mark();
        buffer.put((byte) 6);
        buffer.put((byte) 7);
        // buffer after position(1) mark= [1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer stay position(1)after mark= " + Arrays.toString(buffer.array()));
        buffer.reset();
        buffer.put((byte) 8);
        // buffer after position(1), mark after reset= [1, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer stay position(1)after mark after reset= " + Arrays.toString(buffer.array()));

        // flip gives the pointer position to the position index to limit, and position is set to 0
        // flip is mostly used to read and write files. You can write as much as you read
        buffer.limit(10);// Remove the limit limit first
        buffer.position(3);
        buffer.flip();// position=0, limit=3
        buffer.put((byte) 9);
        buffer.put((byte) 10);
        buffer.put((byte) 11);
        // buffer.put((byte) 12);  Exception: java.nio.BufferOverflowException
        // buffer = flip [9, 10, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("buffer stay flip= " + Arrays.toString(buffer.array()));

        // Reset position=0 and limit=capacity
        buffer.clear();
        for (int i = 0; i < 10; i++) {
            buffer.put((byte) i);
        }
        // buffer in clear= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        System.out.println("buffer stay clear= " + Arrays.toString(buffer.array()));
    }
}

Channel interface

passageway

  • A Channel for reading and writing files. It can be regarded as input and output streams in IO. The difference is that the Channel is bidirectional, readable and writable

classification

  • FileChannel: input and output streams for reading data from files
  • Datagram channel: read and write UDP network protocol data
  • SocketChannel: read and write TCP network protocol data socket (client)
  • ServerSocketChannel: can listen for TCP connections to ServerSocket (server side)
FileChannel class

You can get a subclass object of FileInputStream and FileOutputStream through the getChannel() method

Copy of files
public static void main(String[] args) throws Exception {
    // Requirement: copy a.jpg file
    // Idea: use FileChannel to read the data into the byte buffer array, and then write it from the byte buffer array to the destination file
    // 1. Create byte input stream object and associate data source file path
    FileInputStream fis = new FileInputStream("d:\\img\\a.jpg");

    // 2. Create byte output stream object and associate destination file path
    FileOutputStream fos = new FileOutputStream("d:\\img\\b.jpg");

    // 3. Obtain the FileChannel object through the input stream object
    FileChannel c1 = fis.getChannel();

    // 3. Obtain the FileChannel object through the output stream object
    FileChannel c2 = fos.getChannel();

    // 4. Create a byte buffer array to store the read byte data
    ByteBuffer b = ByteBuffer.allocate(8192);

    // 5. Read byte data circularly
    while (c1.read(b) != -1) {
        // flip: position becomes: 0, and limit becomes: position(8192, but the last time may not be 8192). To ensure that the read data is written out
        // After using flip, you don't need to define a len to intercept. You can refer to the above TCP file operation
        b.flip();

        // 6. In the loop, write out byte data
        c2.write(b);

        // clear: position becomes: 0,limit becomes: capacity(8192) for the next cycle
        b.clear();

    }
    // 7. Close the flow and release resources
    c2.close();
    c1.close();
    fos.close();
    fis.close();
}
FileChannel combined with MappedByteBuffer to achieve efficient read and write

MappedByteBuffer class is a ByteBuffer subclass. It can directly map files to memory and change the reading and writing in hard disk into reading and writing in memory, so it can improve the reading and writing efficiency of large files

  • The map() method of FileChannel gets a MappedByteBuffer
  • MappedByteBuffer map(MapMode mode, long position, long size). Note: map the size bytes from position in the node to the returned mappedbytebuffer.
    • FileChannel.MapMode.READ_ONLY: the obtained image can only be read but not written
    • FileChannel.MapMode.READ_WRITE: the resulting image is readable and writable
    • FileChannel.MapMode.PRIVATE: get a private image
  • Obtain Channel by using RandomAccessFile
    • The Channel obtained by using the input stream can only specify the read mode, that is, "r"
    • The Channel obtained by using the output stream can only specify the write mode, that is, "rw"
    • Only the Channel obtained by RandomAccessFile can enable any of these three modes
public static void main(String[] args) throws Exception {
    long a = System.currentTimeMillis();
    // 1. Create RandomAccessFile object: r means read, rw means read and write
    RandomAccessFile r1 = new RandomAccessFile("d:\\img\\a.rar", "r");
    RandomAccessFile r2 = new RandomAccessFile("d:\\img\\b.rar", "rw");

    // 2. Obtain Channel object
    FileChannel c1 = r1.getChannel();
    FileChannel c2 = r2.getChannel();

    // 3. Get the byte size of the source file
    long size = c1.size();
    System.out.println(size);

    // Circular replication
    // Total file size: size
    // Assume the byte size of each copy: everySize = 500MB
    long everySize = 500 * 1024 * 1024;

    // How many copies are needed in total: count = size% everysize = = 0? size / everySize  :  size / everySize +1
    long count = size % everySize == 0 ? size / everySize : size / everySize + 1;

    // Circular replication
    for (long i = 0; i < count; i++) {
        // Start byte position of each copy: start = i * everySize
        long start = i * everySize;
        // How many bytes are actually copied each time: truesize = size - Start > everysize? everySize :  size - start;
        // This is mainly because the last replication is not equal to the size of each replication everySize
        long trueSize = size - start > everySize ?  everySize :  size - start;

        // 3. Use the Channel to call the map method to obtain the MappedByteBuffer
        MappedByteBuffer m1 = c1.map(FileChannel.MapMode.READ_ONLY, start, trueSize);
        MappedByteBuffer m2 = c2.map(FileChannel.MapMode.READ_WRITE, start, trueSize);

        // 4. Copy the byte data in m1 array to m2
        for (long j = 0; j < trueSize; j++) {
            //System.out.println("j = " + j);
            // Get bytes in m1
            byte b = m1.get();
            // Store the obtained bytes in m2
            m2.put(b);
        }
    }
    // 5. Release resources
    c2.close();
    c1.close();
    r2.close();
    r1.close();
    long b = System.currentTimeMillis();
    System.out.println("time consuming: " +  (b -a)/1000 + "second");
}
SocketChannel and ServerSocketChannel
  • The SocketChannel class is used to connect clients. It is equivalent to: Socket

  • The ServerSocketChannel class is used to connect to the server. It is equivalent to: ServerSocket

/**
 * client
 */
// Access channel
SocketChannel sc = SocketChannel.open();
// Connect server
sc.connect(new InetSocketAddress("127.0.0.1", 6666));
// Create ByteBuffer byte buffer array
ByteBuffer b = ByteBuffer.allocate(1024);
// Stores data in a byte buffer array
b.put("The server,Hello,Do you have an appointment tonight?".getBytes());
// Position is set to 0 and limit is set to the previous position
b.flip();
// Write data to server
sc.write(b);
// Release resources
sc.close();
/**
 * Server
 */
// Get ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
// Binding port number
ssc.bind(new InetSocketAddress(6666));
// Set non blocking
ssc.configureBlocking(false);
// Receive request
SocketChannel sc = null;
while (true) {
    // Receive client connections
    sc = ssc.accept(); // It won't block here
    if (sc == null){
        System.out.println("Not connected yet"); // Loop until the client starts the connection
    }else{
        System.out.println("The connection succeeded!");
        break;
    }
}
// Create ByteBuffer byte buffer array
ByteBuffer b = ByteBuffer.allocate(1024);
// Receive data written by the client
int len = sc.read(b);
System.out.println(new String(b.array(),0,len));
// Release resources
ssc.close();

Selector

selector

  • Multichannel means that the server side listens to multiple "ports" at the same time. Each port listens to multiple client connections
  • Multiplexing means that a Selector can listen to multiple server ports
  • Server side non multiplexing effect

If "multiplexing" is not used, the server needs to open many threads to process the requests of each port. If it is in a high concurrency environment, the system performance will be degraded.

  • Server side multiplexing effect

Using multiplexing, only one thread can process multiple channels, reduce memory occupancy and CPU switching time. It has very important advantages in high concurrency and high-frequency business environment

  • A Selector can monitor events in multiple channels, reduce system burden and improve program execution efficiency

  • Selector selector = Selector.open()

  • channel object. register(Selector sel, int ops) method to implement registration

    • Parameter 1: selector

    • Parameter 2: what events are you interested in when listening to the Channel through the Selector

1. Connection ready – constant: SelectionKey.OP_CONNECT
2. Receive ready – constant: SelectionKey.OP_ACCEPT (ServerSocketChannel can only use this item when registering)
3. Read ready – constant: SelectionKey.OP_READ
4. Write ready – constant: SelectionKey.OP_WRITE

  • be careful
    1. For ServerSocketChannel, only OP can be used during registration_ Accept, otherwise an exception is thrown.
    2. The ServerSocketChannel must be set to non blocking

  • The server creates three channels, listens to three ports at the same time, and registers the three channels in a selector

public static void main(String[] args) throws Exception{
    // Get the ServerSocketChannel object of 3 ports
    ServerSocketChannel ssc1 = ServerSocketChannel.open();
    ServerSocketChannel ssc2 = ServerSocketChannel.open();
    ServerSocketChannel ssc3 = ServerSocketChannel.open();

    // Binding port number
    ssc1.bind(new InetSocketAddress(6666));
    ssc2.bind(new InetSocketAddress(7777));
    ssc3.bind(new InetSocketAddress(8888));

    // Set Channel to non blocking
    ssc1.configureBlocking(false);
    ssc2.configureBlocking(false);
    ssc3.configureBlocking(false);

    // Get selector
    Selector selector = Selector.open();

    // Register the Channel with the selector
    ssc1.register(selector, SelectionKey.OP_ACCEPT);
    ssc2.register(selector, SelectionKey.OP_ACCEPT);
    ssc3.register(selector, SelectionKey.OP_ACCEPT);
    // Mark
}
common method
  • select,selectedKeys,keys

select the method that the server waits for the client to connect

  • Blocking problem:
    • It will be blocked until the first client is connected
    • After connecting to the client, if the client is not processed, the method is in a non blocking state
    • After connecting to the client, if the client is processed, the method will enter the blocking state again
// client
public static void main(String[] args) throws Exception{
    // Get SocketChannel object
    SocketChannel sc = SocketChannel.open();
    // Connect server
    sc.connect(new InetSocketAddress("127.0.0.1", 6666));
    // Create ByteBuffer byte buffer array
    ByteBuffer b = ByteBuffer.allocate(1024);
    // Stores data in a byte buffer array
    b.put("The server,Hello,Do you have an appointment tonight 6666?".getBytes());
    // Position is set to 0 and limit is set to the previous position
    b.flip();
    Thread.sleep(4000);
    // Write data to server
    sc.write(b);
    // Release resources
    sc.close();
}
// Duplicate location code marked from above
while (true) {
    // The server is waiting for the client to connect
    selector.select();
    
    // selectedKeys() method of Selector: get all connected channel collections
    // Get all connected Channel channels
    Set<SelectionKey> keys = selector.selectedKeys();
    System.out.println("Number of all connected channels:"+keys.size());
    
    // keys() method of Selector: get all registered channel collections
    // Get registered connection channel
    Set<SelectionKey> set = selector.keys();
    System.out.println("Registered connection channel:"+set.size());

    // Loop through the connected Channel
    for (SelectionKey key : keys) {
        // SelectionKey encapsulates ServerSocketChannel
        // Get the ServerSocketChannel to which the client wants to connect
        ServerSocketChannel channel = (ServerSocketChannel)key.channel();
        // Processing client requests
        SocketChannel sc = channel.accept();
        // sc.read....

    }
}
  • In fact, the selector is equivalent to a middleman. Let's look at this figure

NIO multiplexing

public class Server {
    public static void main(String[] args) throws Exception{
        // Get the ServerSocketChannel object of 3 ports
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // Binding port number
        ssc1.bind(new InetSocketAddress(6666));
        ssc2.bind(new InetSocketAddress(7777));
        ssc3.bind(new InetSocketAddress(8888));

        // Set Channel to non blocking
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // Get selector
        Selector selector = Selector.open();

        // Register the Channel with the selector
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // The server is waiting for the client to connect
            selector.select();
            // Processing client requests
            // Get all connected Channel channels
            Set<SelectionKey> keys = selector.selectedKeys();
            /**
             * Problem: the Selector puts all connected server objects in a Set collection, but does not delete them after use, resulting in traversing the collection to the point where they have been used 			  *  Like, something's wrong
             * Solution: after use, it should be deleted from the collection. Because it cannot be deleted while traversing, use the iterator for traversal     
        	 */
            // Loop through all connected Channel channels
            /*for (SelectionKey key : keys) {
                // SelectionKey Encapsulated ServerSocketChannel
                // Convert key to ServerSocketChannel object
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                // Processing client requests
                SocketChannel sc = ssc.accept();
                // Read the data written by the client
                ByteBuffer b = ByteBuffer.allocate(1024);
                int len = sc.read(b);
                System.out.println(new String(b.array(),0,len));
                sc.close();

            }*/

            // Get iterator
            Iterator<SelectionKey> it = keys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                // Convert key to ServerSocketChannel object
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                // Processing client requests
                SocketChannel sc = ssc.accept();
                // Read the data written by the client
                ByteBuffer b = ByteBuffer.allocate(1024);
                int len = sc.read(b);
                System.out.println(new String(b.array(),0,len));
                sc.close();
                // delete
                it.remove();
            }
        }
    }
}
Multiplex finishing
  • Server
public class NIOServer {
	private int port = 8888;
    // For character set encoding and decoding
    private Charset charset = Charset.forName("UTF-8");
    // Buffer for receiving data
    private ByteBuffer rBuffer = ByteBuffer.allocate(1024);
    // Buffer for sending data
    private ByteBuffer sBuffer = ByteBuffer.allocate(1024);
    // Used to store the client SocketChannel collection
    private Map<String, SocketChannel> clientMap = new HashMap();
    // Used to listen for channel events
    private static Selector selector;
    public NIOServer(int port) {
        this.port = port;
        try {
            init();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // initialize server
    private void init() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(port));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Server startup, port:" + port);
    }
    /**
     * The server side polls and listens. The select method is blocked until a related event occurs or a timeout occurs
     */
    public void listen() {
        while (true) {
            try {
                selector.select();   // The return value is the number of events triggered this time
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //selectionKeys.forEach(selectionKey -> handle(selectionKey));
                for(SelectionKey sk : selectionKeys){
    				handle(sk);
    			}
                selectionKeys.clear(); // Clear processed events
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Handling events
     */
    private void handle(SelectionKey selectionKey) {
        try {
            // There are clients to connect to
            if (selectionKey.isAcceptable()) {
                ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                SocketChannel client = server.accept();
                client.configureBlocking(false);
                client.register(selector, SelectionKey.OP_READ);
                clientMap.put(getClientName(client), client);
            }
            // The client sent a message
            else if (selectionKey.isReadable()) {
                SocketChannel client = (SocketChannel) selectionKey.channel();
                rBuffer.clear();
                int bytes = client.read(rBuffer);
                if (bytes > 0) {
                    rBuffer.flip();
                    String receiveText = String.valueOf(charset.decode(rBuffer));
                    System.out.println(client.toString() + ":" + receiveText);
                    dispatch(client, receiveText);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * Forward messages to each client
     */
    private void dispatch(SocketChannel client, String info) throws IOException {
        if (!clientMap.isEmpty()) {
            for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                SocketChannel temp = entry.getValue();
                if (!client.equals(temp)) {
                    sBuffer.clear();
                    sBuffer.put(charset.encode(getClientName(client) + ":" + info));
                    sBuffer.flip();
                    temp.write(sBuffer);
                }
            }
        }
    }
    /**
     * Generate client name
     */
    private String getClientName(SocketChannel client){
        Socket socket = client.socket();
        return "[" + socket.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode()) + "]";
    }
    public static void main(String[] args) {
        NIOServer server = new NIOServer(7777);
        server.listen();
    }
}
  • client
public class NIOClient {
	// Server address
	private InetSocketAddress SERVER;
	// Buffer for receiving data
	private ByteBuffer rBuffer = ByteBuffer.allocate(1024);
	// Buffer for sending data
	private ByteBuffer sBuffer = ByteBuffer.allocate(1024);
	// Used to listen for channel events
	private static Selector selector;
	// buffer for encoding / decoding
	private Charset charset = Charset.forName("UTF-8");
	public NIOClient(int port) {
		SERVER = new InetSocketAddress("localhost", port);
		try {
			init();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	// Initialize client
	private void init() throws IOException {
		SocketChannel socketChannel = SocketChannel.open();
		socketChannel.configureBlocking(false);
		selector = Selector.open();
		socketChannel.register(selector, SelectionKey.OP_CONNECT);
		socketChannel.connect(SERVER);
		while (true) {
			selector.select();
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			//selectionKeys.forEach(selectionKey -> handle(selectionKey));
			
			for(SelectionKey sk : selectionKeys){
				handle(sk);
			}
			
			selectionKeys.clear(); // Clear processed events
		}
	}
	/**
	 * Handling events
	 */
	private void handle(SelectionKey selectionKey) {
		try {
			// Connection ready event
			if (selectionKey.isConnectable()) {
				SocketChannel client = (SocketChannel) selectionKey.channel();
				if (client.isConnectionPending()) {
					client.finishConnect();
					System.out.println("Connection succeeded!");
					// Start the thread to listen for client input
					new Thread() {
						@Override
						public void run() {
							while (true) {
								try {
									sBuffer.clear();
									Scanner scanner = new Scanner(System.in);
									String sendText = scanner.nextLine();
									System.out.println(sendText);
									sBuffer.put(charset.encode(sendText));
									sBuffer.flip();
									client.write(sBuffer);
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						}
					}.start();
				}
				// Register readable events
				client.register(selector, SelectionKey.OP_READ);
			}
			// Readable events, with information sent from the server, read and output to the screen
			else if (selectionKey.isReadable()) {
				SocketChannel client = (SocketChannel) selectionKey.channel();
				rBuffer.clear();
				int count = client.read(rBuffer);
				if (count > 0) {
					String receiveText = new String(rBuffer.array(), 0, count);
					System.out.println(receiveText);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new NIOClient(7777);
	}
}

Topics: Java network Back-end server