Learning network programming from scratch in Java (BIO)

summary

Network programming is inseparable from data transmission, and data transmission uses IO stream in java. The most commonly used are InputStream byte input stream and OutputStream ` ` character output stream, BufferReader character input stream and BufferWirter ` ` character output stream. Here are some important concepts

  1. O S I seven layer number according to model type \color{green}{OSI seven layer data model} OSI seven layer data model
    OSI has developed a framework for data communication mark accurate \color{red} {standard} Standard, this mark accurate \color{red} {standard} The standard is a seven layer model, application layer, presentation layer, session layer, transport layer, network layer, data link layer, physical layer
    Each time has its own specific role, and the data will be packaged once. Socket programming is at the transport layer. TCP and UDP protocols are transport layer protocols.
  2. with step And different step \color{green} {synchronous and asynchronous} Synchronous and asynchronous
    Synchronization and asynchrony: synchronization means that when calling, the caller will not wait for the return value before processing is completed. In short, the caller actively waits for the result to return, while asynchrony is on the contrary. When calling, the result will not be obtained immediately, but the client will notify when it will be returned.
  3. Resistance stopper And wrong Resistance stopper \color{green} {blocking and non blocking} Blocking and non blocking
    Blocking and non blocking: the difference between blocking and non blocking is the state of the caller while waiting for the return value. Blocking means that the current thread will not do anything else before waiting for the result to be returned. It will only wait obediently, rather than blocking. On the contrary, it can do other things during the waiting process and wait until the asynchronous notification is returned.
  4. B I O yes number according to with step Resistance stopper type \color{green}{BIO is data synchronization blocking} BIO is a data synchronization blocking type
    It means that when the server receives the request from the client, it will wait for some columns of the client to be operated (including reading and writing) before making requests from other clients. And will always be blocked and do nothing. Until the modified client exits, connect and read / write to another client

text

Demonstrates the sample code of the server during synchronization

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
    public static void main(String[] args) {
        try {
            //1. Create ServerSocket locally and bind port 9999
            ServerSocket server = new ServerSocket(9999);
            //2 accept client connection (this method is blocking. If there is no connection, it will wait all the time)
            Socket socket = server.accept();
            //3 get the byte input stream of the client
            InputStream inputStream = socket.getInputStream();
            //Input byte character into stream
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String msg = "";
            //4 accept the information of the client by line (this method is blocking and will always read the information of the client)
            while((msg = bufferedReader.readLine())!=null){
                System.out.println("Information received from client"+msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Demonstrates the code of the synchronization client

package com.demo.bio.one;

import java.io.IOException;
import java.io.OutputStream;

import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        //Create client socket
        Socket socket = new Socket();
        try {
            //Connect to local port 9999
            socket.connect(new InetSocketAddress(9999),1000);
            //Get byte output stream
            OutputStream outputStream = socket.getOutputStream();
            //Transfer byte output to print stream 
            PrintStream printStream =new PrintStream(outputStream);
            //Get keyboard input
            Scanner scanner = new Scanner(System.in);
            while(scanner.hasNext()){
                String next = scanner.next();
                //Output data
                printStream.println(next);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Start the server first and then the client. At this time, the customer simply enters text in the console, and then the server can receive the information and print it on the console of the server. If there is a server with socket = server, it will be unable to connect at one time When accepting (), after accepting a client connection, the code at this time will not be executed. If it is necessary to execute, we will use the server code at this time

public class Server {
    public static void main(String[] args) {
        try {
            //1. Create ServerSocket locally and bind port 9999
            ServerSocket server = new ServerSocket(9999);
            //2 accept client connection (this method is blocking. If there is no connection, it will wait all the time)
            while(true){
	            Socket socket = server.accept();
	            //3 get the byte input stream of the client
	            InputStream inputStream = socket.getInputStream();
	            //Input byte character into stream
	            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
	            String msg = "";
	            //4 accept the information of the client by line (this method is blocking and will always read the information of the client)
	            while((msg = bufferedReader.readLine())!=null){
	                System.out.println("Information received from client"+msg);
	            }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

At the same time, when two clients connect to the server, they will find that the data of the second client cannot reach the server anyway. This is because BIO is blocked. When the main thread processes the first client, as long as the current client is online, it cannot process other clients. At this time, if the server is required to have multiple clients at the same time, you need to use Line Cheng \color{red} {thread} Threads handle a client separately.

Multi client request server code modification

package com.demo.bio.two;

import java.net.ServerSocket;
import java.net.Socket;
public class Server {
    public static void main(String[] args) {
         try{
             ServerSocket serverSocket = new ServerSocket(9999);
             while (true){
                 Socket socket = serverSocket.accept();
                 //When a new client arrives, start a new thread to process it. The main thread then continues to accept new clients
                 new Thread(  new HandlerSocket(socket)).start();
             }
         }catch (Exception e){
             e.printStackTrace();
         }
    }
}

Client processing request HandlerSocket thread

package com.demo.bio.two;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class HandlerSocket implements Runnable {
    private Socket socket;
    public HandlerSocket(Socket socket) {
        this.socket = socket;
    }
    public void run() {
       try(InputStream inputStream = socket.getInputStream()){
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
           String msg =null;
           while((msg=bufferedReader.readLine())!=null){
               System.out.println("Client message received"+msg);
           }
       }catch (Exception e){
       }
    }
}

The original client code remains unchanged. At this time, data from different clients can be accepted and input in different client consoles



Then a problem will arise. With the increasing number of clients, the number of threads on the server increases with the increasing number of clients. If the JVM cannot create stack space for new threads when the number of threads reaches a certain value, an error of ` ` ` ` outofmemoroy ` ` ` will occur. The system crashed. This is what we don't want to see. At this time, we need to control the number of threads. If we control the number of threads, since threads are used to handle the connection of the client, we will use thread related technology and thread pool technology to transform the code of the server

Thread pool transformation server code

package com.demo.bio.three;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket server = new ServerSocket(9999)) {
        //Create a thread pool
            HandlerSocketThreadPools pools = new HandlerSocketThreadPools(3,10);
            while(true){
                Socket socket = server.accept();
                pools.execute(new HandlerSocket(socket));
            }
        }catch (Exception e){
          e.printStackTrace();
        }
    }
}

The code of HandlerSocketThreadPools is as follows

package com.demo.bio.three;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class HandlerSocketThreadPools {
    private ThreadPoolExecutor threadPoolExecutor;
    public HandlerSocketThreadPools(int maxSize,int size){
        threadPoolExecutor= new ThreadPoolExecutor(3,maxSize,120, TimeUnit.MICROSECONDS, new ArrayBlockingQueue(size));
    }
    public void execute(Runnable runnable){
        threadPoolExecutor.execute(runnable);
    }
}

At this time, the original code can be executed in a certain number of thread pools, but the number of core threads is set to 3, which means that only three clients can connect at most. When the fourth client connects, it can only enter the blocking queue and wait. At this time, NIO can only be used to solve it.

Through this article, we summarize.
BIO is an IO model of synchronous blocking. A client corresponds to this thread. If there are too many clients, it is easy to crash the program.

Now use BIO to develop a chat room

Chat server code

package com.demo.bio.chatroom;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
public class Server {
     //Create a thread safe collection and save all online clients
    public static CopyOnWriteArrayList<Socket> onLine = new CopyOnWriteArrayList();
    public static void main(String[] args) {
        try{
            ServerSocket server = new ServerSocket();
            server.bind(new InetSocketAddress(9999));
            while(true){
                Socket socket = server.accept();
                //Save socket to online list
                onLine.add(socket);
                new Thread(new HandlerSocket(socket)).start();
            }
        }catch (Exception e){
              e.printStackTrace();
        }
    }
}

The server handles the client thread separately

package com.demo.bio.chatroom;

import java.io.*;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

public class HandlerSocket  implements Runnable{
     //Currently connected clients
    private Socket socket;
    private int port;

    public HandlerSocket(Socket socket){
        this.socket = socket;
        this.port = socket.getPort();
    }
    @Override
    public void run() {
        System.out.println("client"+port+"Join the chat room");
        try{
           //Gets the character input stream of the current client
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg;
            //Send new client online messages to all online clients
            sendLoginToOnline(socket);
            //Read the data sent by the client
            while((msg=reader.readLine())!=null){
                System.out.println("client:"+port+"Messages sent"+msg);
                sendMsgToOnline(socket,msg);
            }
            reader.close();
        }catch (Exception e){
            e.printStackTrace();
            Server.onLine.remove(socket);
        }
    }
   //Send online messages to all clients
    private void sendLoginToOnline(Socket socket) {
        CopyOnWriteArrayList<Socket> onLine = Server.onLine;
        for (Socket data : onLine) {
            if(socket!=data){
                try{
                    PrintStream printStream = new PrintStream( data.getOutputStream());
                    printStream.println(socket.getPort()+"Join the chat room");
                    printStream.flush();
                }catch (Exception e){
                    System.out.println("Error sending data");
                    onLine.remove(data);
                }
            }

        }
    }
   //Want to forward messages to all online clients
    private void sendMsgToOnline(Socket socket,String msg) {

        CopyOnWriteArrayList<Socket> onLine = Server.onLine;
        for (Socket data : onLine) {
            if(socket!=data){
                try{
                    PrintStream printStream = new PrintStream( data.getOutputStream());
                    printStream.println(socket.getPort()+":"+msg);
                    printStream.flush();
                }catch (Exception e){
                    System.out.println("Error sending data");
                    onLine.remove(data);
                }
            }

        }
    }
}

Client code

package com.demo.bio.chatroom;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {

    public static void main(String[] args) {
        Socket socket=null;
        PrintStream printStream = null;
        try{
             socket = new Socket();
            socket.connect(new InetSocketAddress(9999));
             //Start a thread to read the messages forwarded by the server to the client
            new Thread(new HandlerRead(socket)).start();

            OutputStream outputStream = socket.getOutputStream();
             printStream = new PrintStream(outputStream);
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String msg = scanner.nextLine();
                printStream.println(msg);
                printStream.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
            try {
                socket.close();
                printStream.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

The client independently accepts the thread that the server forwards the message

package com.demo.bio.chatroom;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class HandlerRead implements Runnable {

    private Socket socket;


    public HandlerRead(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
            try{
                   InputStream inputStream = socket.getInputStream();
                   BufferedReader read = new BufferedReader(new InputStreamReader(inputStream));
                   String msg;
                   while((msg=read.readLine())!=null){
                       System.out.println(msg);
                   }
               
            }catch (Exception e){
                  e.printStackTrace();
            }
    }
}

Keywords: Java

Added by davemwohio on Tue, 08 Feb 2022 00:36:57 +0200