One-stop learning Java network programming Comprehensive understanding of BIO_NIO_AIO, learning notes (4)

Hello everyone, I am a 方圆
BIO chat room that is very well implemented


1. Conceptual illustration

Insert picture description here

  • BIO model: For every request from the client, the server must have a thread to process the request separately. Typically 一请求一应答, before java version 1.4
  • For the chat room server, it has multiple threads, one of which is the Acceptor线程(ChatServer) on the picture. What it achieves is to continuously respond to requests from the client and create a distribution processing thread; for each request from the client, there is a separate Processing thread
  • For the client, it has two threads, one thread and the server 建立连接,并接收来自服务器的消息; the other thread is used处理用户的输入,并将消息发送到服务器

1.1 Optimization of pseudo-asynchronous

Insert picture description here

  • The reason for using the thread pool is to prevent a large number of users from responding, causing the server to be overloaded, to realize the reuse of threads, to reduce the resources caused by the continuous creation and deletion of threads, which also realizes the pseudo asynchronous IO communication of BIO

I/O model series three: IO communication model BIO NIO AIO


2. Key points in the chat server

2.1 Field

Insert picture description here

2.2 How to forward messages

Insert picture description here

2.3 Add customer method

Insert picture description here

2.4 How to remove customers

Insert picture description here

2.5 Server main thread tasks

Insert picture description here

2.6 Server processing thread tasks

  • Implement the Runnable interface, and then pass it to the thread pool for execution when it is executed.
    Insert picture description here

3. Key points of chat room client

3.1 Send a message to the server and let the server forward to others

Insert picture description here

3.2 Receiving messages from the server

Insert picture description here

3.3 Main thread tasks

Insert picture description here

3.4 Processing thread tasks

  • Implement Runnable interface, start when thread is created
    Insert picture description here

4. Test results

Server side information
Insert picture description here


5. Complete code

5.1 Server

package Server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ChatServer {
    
    
    private int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";

    private ServerSocket serverSocket;
    //存储已经连接的客户们
    private Map<Integer, Writer> connectedClients;
    private ExecutorService executorService;

    public ChatServer() {
    
    
        this.connectedClients = new HashMap<>();
        this.executorService = Executors.newFixedThreadPool(10);
    }

    //添加客户端
    public synchronized void addClient(Socket socket) throws IOException {
    
    
        if(socket != null){
    
    
            int key = socket.getPort();
            Writer value = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            connectedClients.put(key,value);
            System.out.println("客户" + key + ":已经连接");
        }
    }

    //移除客户端
    public synchronized void removeClient(Socket socket) throws IOException {
    
    
        if(socket != null){
    
    
            int port = socket.getPort();
            if(connectedClients.containsKey(port)){
    
    
                //移除用户的时候要进行关闭
                connectedClients.get(port).close();
                connectedClients.remove(port);
                System.out.println("客户端" + port + ":已经断开连接");
            }
        }
    }

    //将消息转发给其他用户
    public synchronized void forwardMessage(Socket socket,String fwdMsg) throws IOException {
    
    
        for(Integer port : connectedClients.keySet()){
    
    
            //不转发给自己
            if(!port.equals(socket.getPort())){
    
    
                Writer writer = connectedClients.get(port);
                writer.write(fwdMsg);
                writer.flush();
            }
        }
    }

    //检查是否退出
    public boolean readyToQuit(String msg){
    
    
        return QUIT.equals(msg);
    }

    public void close(){
    
    
        if(serverSocket != null){
    
    
            try {
    
    
                serverSocket.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    //启动
    public void start(){
    
    
        try {
    
    
            //绑定监听端口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("聊天室服务器已经成功启动!");

            while (true){
    
    
                Socket socket = serverSocket.accept();

                //为每个socket创建一条单独的线程进行处理
                //new Thread(new ChatHandler(socket,this)).start();
                executorService.execute(new ChatHandler(socket,this));
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            close();
        }
    }

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

package Server;

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

public class ChatHandler implements Runnable {
    
    

    private Socket socket;
    private ChatServer chatServer;

    public ChatHandler(Socket socket, ChatServer chatServer) {
    
    
        this.socket = socket;
        this.chatServer = chatServer;
    }

    @Override
    public void run() {
    
    
        try {
    
    
            //添加对象
            chatServer.addClient(socket);

            //读取用户发送的信息
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            String msg = null;
            //必须要读取到换行符
            while ((msg = reader.readLine()) != null){
    
    
                String fwdMsg ="客户端" + socket.getPort() + ":" + msg + "\n";
                chatServer.forwardMessage(socket,fwdMsg);
                System.out.print(fwdMsg);

                //检查是否退出
                if(chatServer.readyToQuit(msg)){
    
    
                    break;
                }
            }

        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            try {
    
    
                chatServer.removeClient(socket);
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

5.2 Client

package Client;

import Server.ChatServer;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

public class ChatClient {
    
    
    private String DEFAULT_SERVER_HOST = "127.0.0.1";
    private int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";

    private Socket socket;
    private BufferedReader reader;
    private BufferedWriter writer;

    //发送消息给服务器
    public void sendMsg(String msg) throws IOException {
    
    
        //输出流没有关闭的情况
        if(!socket.isOutputShutdown()){
    
    
            writer.write(msg + "\n");
            writer.flush();
        }
    }

    //接受消息从服务器
    public String receiveMsg() throws IOException {
    
    
        if(!socket.isInputShutdown()){
    
    
            return reader.readLine();
        }
        return null;
    }

    //检查是否退出
    public boolean readyToQuit(String msg){
    
    
        return QUIT.equals(msg);
    }

    //关闭
    public void close(){
    
    
        try {
    
    
            writer.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    //启动开关
    public void start(){
    
    
        try {
    
    
            //创建socket
            socket = new Socket(DEFAULT_SERVER_HOST,DEFAULT_PORT);
            //创建io流
            writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );

            //处理用户的输入的线程
            new Thread(new UserInputHandler(this)).start();

            //监听从服务器来的消息
            String msg = null;
            while ((msg = receiveMsg()) != null){
    
    
                System.out.println(msg);
            }
        } catch (UnknownHostException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            close();
        }
    }


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

package Client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class UserInputHandler implements Runnable{
    
    
    private ChatClient chatClient;

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


    @Override
    public void run() {
    
    
        //等待用户输入信息
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(System.in)
        );

        while (true){
    
    
            try {
    
    
                String msg = reader.readLine();
                //向服务器发送消息
                chatClient.sendMsg(msg);
                if(chatClient.readyToQuit(msg))
                    break;
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}


Finally understood!

Guess you like

Origin blog.csdn.net/qq_46225886/article/details/107493129