java Network Programming Reality - BIO-based pseudo-asynchronous, high concurrency, full duplex, long connection continuous message IO network programming

Preface

TCP is a connection-oriented communication protocol through Three-time handshake Set up a connection and remove the connection when the communication is complete. Since TCP is connection-oriented, it can only be used for end-to-end communication.

TCP provides a reliable data stream Service, which uses "affirmative acknowledgement with retransmit" technology to achieve the reliability of transmission.TCP also uses a method called "sliding window" for traffic control, which actually represents the receiving capability to limit the sender's sending speed.

If there are already packed TCP packets in an IP packet, then IP will send them to the TCP layer''up''.TCP sorts the packages and checks for errors, while connecting virtual circuits.TCP packets include sequence numbers and acknowledgments, so packets received out of sequence can be sorted, and corrupted packets can be retransmitted.

TCP sends its information to higher-level applications, such as Telnet's service and client programs.Applications take turns sending information back to the TCP layer, which then sends it down to the IP layer, device drivers and physical media, and finally to the receiver.


Three handshakes based on TCP communication

Native Network Programming BIO

The server provides IP and listening ports, the client initiates a connection request through the connection operation to the address that the server wants to listen on, and connects through three handshakes. If the connection is successfully established, both sides can communicate through the socket.

In the development of traditional synchronous blocking model, ServerSocket is responsible for binding IP addresses and starting listening ports; Socket is responsible for initiating connection operations.When the connection is successful, both sides synchronize blocking communication through input and output streams.(

Traditional BIO communication model: The server which uses the BIO communication model, usually has a separate Acceptor thread responsible for monitoring the connection of the client. It receives the connection request from the client and creates a new thread for each client to process the link. After processing is completed, it returns the response to the client through the output stream and the thread is destroyed.This is the typical one-request-one-response model.

The biggest problem with this model is the lack of flexibility. When the concurrent access of the client increases, the number of threads on the server side is proportional to the concurrent access of the client side by 1:1. Threads in Java are also valuable system resources. After the number of threads expands rapidly, the performance of the system will decrease dramatically. As the access continues to increase, the system will eventually die-out.

To improve this one-connection-threading model, we can use thread pools to manage these threads, implementing a model where one or more threads process N clients (but the underlying layer still uses synchronous blocking I/O), commonly referred to as the pseudo-asynchronous I/O model.


Code Actual

With the above network communication knowledge and basic understanding of the BIO model, we enter the code reality: to implement a BIO-based pseudo-asynchronous, high concurrency, full duplex, Long-connected server programming model.


Server-side implementation

/**
 * @author andychen https://blog.51cto.com/14815984
 * BIO Server Side
 * */
public class Server {
    /**
     * Connection Handling Request Service Pool
     * */
    private static final ExecutorService executorService  = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    /*Call to implement server-client TCP communication*/
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket();
            //Create Server Address
            SocketAddress socketAddress = new InetSocketAddress(Constant.SERVER, Constant.SERV_PORT);
            //Bind socket server IP+port
            serverSocket.bind(socketAddress);

            //Loop monitoring
            System.out.println("================Start listen message===========================");
            for (;;){
                //Receive data: This will be blocked before the message returns
                Socket result = serverSocket.accept();
                //Received data is processed in a separate thread
                executorService.execute(new ServerTask(result));
            }
        } finally {
            if(null != serverSocket && !serverSocket.isClosed()){
                serverSocket.close();
            }
        }
    }

    /*Define server task threads*/
    static class ServerTask implements Runnable{
        private Socket socket = null;
        private List<String> msgList = new LinkedList<>();
        public ServerTask(Socket socket) {
            this.socket = socket;
        }
        /*Processing read-in and write-out message data*/
        @Override
        public void run() {
            String msg = null;
            String newMsg = null;
            //ARM Writing
            try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
                OutputStream outputStream = new ObjectOutputStream(this.socket.getOutputStream())){
                 //Read the returned data
                 while (!Thread.interrupted()){
                     msg = bufferedReader.readLine();
                     System.out.println("Client sent message: "+msg);

                     //Two-way message feedback
                     newMsg = "Server received the sent message: "+msg;
                     ((ObjectOutputStream) outputStream).writeUTF(newMsg);
                     outputStream.flush();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {
                if(!this.socket.isClosed()){
                    try {
                        this.socket.close();
                        this.socket = null;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
             }
        }
    }
}


Client implementation


/**
 * @author andychen https://blog.51cto.com/14815984
 * BIO Client
 * */
public class Client {
    /**Send message to server
     * Receive server feedback at the same time
     * */
    public static void main(String[] args) {
         Socket socket = null;
         String msg = null;
         InputStream inputStream = null;

        PrintWriter printWriter = null;
        Scanner scanner = null;
        try {
            //Define server-side addresses
            SocketAddress address = new InetSocketAddress(Constant.SERVER, Constant.SERV_PORT);
            socket = new Socket();
            //Connect server side
            socket.connect(address);
            if(socket.isConnected()){
                try{
                    printWriter = new PrintWriter(socket.getOutputStream());
                    inputStream = new ObjectInputStream(socket.getInputStream());
                    scanner = new Scanner(System.in);
                    do {
                        msg = scanner.nextLine();
                        printWriter.println(msg);
                        printWriter.flush();

                        //confirmation message
                        msg = ((ObjectInputStream) inputStream).readUTF();
                        System.out.println(msg);
                    }while (!Constant.EXIT_TAG.equals(msg));
                }finally {
                    printWriter.close();
                    printWriter = null;
                    inputStream.close();
                    inputStream = null;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != socket && !socket.isClosed()){
                try {
                    socket.close();
                    socket = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


End-to-end persistent messaging results


conclusion

Combining the above communication mechanism, principles and core implementation ideas, we can expand the application of this model.Next time, based on these ideas and implementations, we will write down the core business and extensions of a synchronization framework defined by RPC.

Tags: Programming socket network Java

Posted on Wed, 17 Jun 2020 16:12:09 -0400 by gurjit