Manually build I / O network communication framework 2: BIO programming model to achieve group chat Manually build I / O network communication framework 1: Socket and ServerSocket entry combat, to achieve single chat

Chapter 1: Manually Building an I / O Network Communication Framework 1: Getting Started with Socket and ServerSocket, Realizing Single Chat

  In the first chapter, Socket and ServerSocket are used to implement network communication. In this chapter, the BIO programming model is used to upgrade and implement group chat rooms.

  

  As shown in the figure: when a client request comes in, the receiver will assign a worker thread to the client, and the worker thread will handle the client's operation full-time. In the previous chapter, the server ran to serve this client after receiving the client request, so when other requests came in, it could not be processed.

  Seeing this picture, it is easy to think of the thread pool. BIO is a relatively simple model. The key to implementing it is also the thread pool.

  Before putting on the code, let's make clear about the role of each class to avoid confusion. More detailed instructions are written in the notes.

  Service-Terminal:

  ChatServer: This class acts like Acceptor in the picture. It has two key global variables, one is a Map that stores online user information, and the other is a thread pool. This class will listen to the port, receive the client's request, and then assign a worker thread to the client. It will also provide some common tool methods for each worker thread to call, such as: sending messages, adding online users, etc.

  ChatHandler: This class is the class of worker threads. In this project, its job is very simple: forward the received messages to other clients, and of course there are some small functions, such as adding \ removing online users.

  Client:

  Compared with the server, the client's changes are smaller, mainly to divide the function of waiting for user input information to other threads, otherwise this function will always block the main thread, resulting in failure to receive messages from other clients.

  ChatClient: The client startup class, which is the main thread, will connect to the server through Socket. Two tool methods are also provided: sending messages and receiving messages.

  UserInputHandler: a thread dedicated to waiting for user input information. Once information is entered, it is sent to the server immediately.

  First create two packages to distinguish between client and server, client and server

  ChatServer on the server side :

public class ChatServer {
    private int DEFAULT_PORT = 8888;
    /**
     * Create a Map to store online user information. This map can count online users, and for these users can forward messages sent by other users
     * Because there will be multiple threads operating this map, use ConcurrentHashMap for safety
     * Here, the key is the port number of the client, but in practice, the port number will definitely not be used to distinguish users. If it is web, session is generally used.
     * value is Writer of IO, used to store the message sent by the client
     */
    private Map<Integer, Writer> map=new ConcurrentHashMap<>();
    /**
     * Create a thread pool, the thread limit is 10, if the 11th client request comes in, the server will receive but will not allocate threads to handle it.
     * The chat history of the first 10 clients is invisible. When a client goes offline, the 11th client will be assigned a thread and the server shows online
     * You can set 10 to be smaller, test it
     * * / 
    private ExecutorService executorService = Executors.newFixedThreadPool ( 10 );
     // Add a client to the map when the client connects 
    public  void addClient (Socket socket) throws IOException {
         if (socket! = null ) {
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            map.put(socket.getPort(), writer);
            System.out.println("Client["+socket.getPort()+"]:Online");
        }
    }

    // Remove the client from the map when disconnecting 
    public  void removeClient (Socket socket) throws Exception {
         if (socket! = Null ) {
             if (map.containsKey (socket.getPort ())) {
                map.get(socket.getPort()).close();
                map.remove(socket.getPort());
            }
            System.out.println("Client[" + socket.getPort() + "]Offline");
        }
    }

    // Forward client messages, this method is to send the message to all other clients online 
    public  void sendMessage (Socket socket, String msg) throws IOException {
         // Traverse online client 
        for (Integer port: map.keySet () ) {
             // Send to other online clients 
            if (port! = Socket.getPort ()) {
                Writer writer = map.get(port);
                writer.write(msg);
                writer.flush();
            }
        }
    }

    // Receive client request and assign Handler to handle request 
    public  void start () {
         try (ServerSocket serverSocket = new ServerSocket (DEFAULT_PORT)) {
            System. Out .println ( " Server Start, The Port is: " + DEFAULT_PORT);
             while ( true ) {
                 // Waiting for client connectionSocket 
                socket = serverSocket.accept ();
                 // Assign a ChatHandler thread 
                executorService for the client execute ( new ChatHandler ( this , socket));
            }
        } catch (IOException e) {
            e.printStackTrace ();
        }
    }

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

  ChatHandler on the server side:

public class ChatHandler implements Runnable {
    private ChatServer server;
    private Socket socket;

    // Constructor, ChatServer assigns Handler thread through this 
    public ChatHandler (ChatServer server, Socket socket) {
         this .server = server;
         this .socket = socket;
    }

    @Override
    public  void run () {
         try {
             // Add this client to the map 
            server.addClient (socket);
             // Read the message sent by this client 
            BufferedReader reader = new BufferedReader (
                     new InputStreamReader (socket.getInputStream ())
            );
            String msg = null ;
             while ((msg = reader.readLine ())! = Null ) {
                 // This stitching is to allow other clients to see who sent the message. 
                String sendmsg = "Client [" + socket. getPort () + "]:" + msg;
                 // The server prints this message 
                System.out.println (sendmsg);
                 // Forward the received message to other online clients 
                server.sendMessage (socket, sendmsg + "\ n " );
                 if (msg.equals (" quit " )) {
                     break ;
                }
            }
        } catch (IOException e) {
            e.printStackTrace ();
        } finally {
             // If the user exits or an exception occurs, remove the client from the map 
            try {
                server.removeClient(socket);
            } catch (Exception e) {
                e.printStackTrace ();
            }
        }
    }
}

  Client ChatClient:

public  class ChatClient {
     Private the BufferedReader Reader;
     Private BufferedWriter, Writer;
     Private the Socket socket;
     // send a message to the server 
    public  void sendToServer (String MSG) throws IOException {
         // before sending, it is determined whether or not to close the socket output stream 
        IF (! socket.isOutputShutdown ()) {
             // If not closed, put the message entered by the user into the 
            writer.writer.write (msg + "\ n" );
            writer.flush();
        }
    }
    // Receive message from server 
    public String receive () throws IOException {
        String msg = null ;
         // Determine whether the socket input stream is closed 
        if (! Socket.isInputShutdown ()) {
             // If not closed, you can read the message sent by the server through the reader. Note: If the message is not read, the thread will block here 
            msg = reader.readLine ();
        }
        return msg;
    }

    public  void start () {
         // Create connection with service 
        try {
            socket = new Socket("127.0.0.1", 8888);
            reader=new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            writer=new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            // Create a new thread to listen to user input messages 
            new Thread ( new UserInputHandler ( this )). Start ();
             / **
             * Keep reading the information of other clients forwarded by the server
             * Record the pits you have stepped on before:
             * Here, you must create a msg to receive information. If you use the receive () method to judge and output receive () directly, some messages will not be displayed.
             * Because receive () is blocked before returning, it will return once the message is received, that is, while is blocked here, and once there is a message, it will enter the while
             * If the output is receive () at this time, the information obtained last time will be lost, and then blocked in System.out.println
             * */
            String msg=null;
            while ((msg=receive())!=null){
                System.out.println(msg);
            }
        } catch (IOException e) {
            e.printStackTrace ();
        }finally {
            try {
               if(writer!=null){
                   writer.close();
               }
            } catch (IOException e) {
                e.printStackTrace ();
            }
        }
    }

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

  Client UserInputHandler:

public class UserInputHandler implements Runnable {
    private ChatClient client;

    public UserInputHandler(ChatClient client) {
        this.client = client;
    }

    @Override
    public  void run () {
         try {
             // Receive user input message 
            BufferedReader reader = new BufferedReader (
                     new InputStreamReader (System.in)
            );
            // Non-stop access to the System.in in the reader to achieve the effect of waiting for user input 
            while ( true ) {
                String input = reader.readLine ();
                 // Send a message to the server 
                client.sendToServer (input);
                 if (input.equals ("quit" ))
                     break ;
            }
        } catch (IOException e) {
            e.printStackTrace ();
        }
    }
}

 

  Run the test:

  Compile through javac by opening the terminal. If you are encoding on IDEA, you may report encoding errors. Just add -encoding utf-8 after javac and then connect to the java file.

  After compiling and running, when running through java, I encountered another pit. It will report an error that the main class cannot be found. It turns out that because two packages are added, the package name must be added in front of the class file name. For example, currently in the src directory, there are two packages, client and server, to be run like this: java client.XXXX. But I used to run java in the client folder, but it doesn't work either. I don't know why.

  Then test:

  1. First run ChatServer in a terminal and turn on the server

  2. Open ChatClient in the second terminal and call it A for now, then the server terminal displays:

  3. Similarly, open ChatClient in the third terminal and call it B for now. At this time, the server displays:

  4. Enter hi in A, in addition to the server will print hi, B will also display, the port number in the picture is different from the previous one, because there is a small problem in the middle, the first three screenshots and the following are not running at the same time of. In practice, the same client will display the same port number:

  5. When the client enters quit, it will be disconnected. Finally, the server displays:

Guess you like

Origin www.cnblogs.com/lbhym/p/12681787.html