Java IO : BIO阻塞模型

在这里插入图片描述
基于BIO的多人聊天室设计与实现

在这里插入图片描述
在这里插入图片描述
服务端实现:

package top.onefine.demo.socket.nio.server;

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * @author one fine<br/>
 */
@Slf4j
public class ChatServer {
    
    

    private final int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";
    private ServerSocket serverSocket;
    private final Map<String, Writer> connectedClients;

    public ChatServer() {
    
    
        this.connectedClients = new HashMap<>();
    }

    public synchronized void addClient(Socket socket) throws IOException {
    
    
        if (socket != null) {
    
    
            InetAddress address = socket.getInetAddress();
            int port = socket.getPort();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            this.connectedClients.put(address + ":" + port, writer);
            log.info("客户端[{}:{}]成功连接到服务器...", address, port);
        }
    }

    public synchronized void removeClient(Socket socket) throws IOException {
    
    
        if (socket != null) {
    
    
            InetAddress address = socket.getInetAddress();
            int port = socket.getPort();
            String key = address + ":" + port;
            if (this.connectedClients.containsKey(key)) {
    
    
                Writer writer = connectedClients.get(key);
                writer.close();  // 关闭
                this.connectedClients.remove(key);
                log.info("客户端[{}:{}]断开与服务器连接.", address, port);
            }
        }
    }

    /**
     * 转发消息
     * @param socket 消息发送者
     * @param fwdMsg 需要转发的消息内容
     */
    public synchronized void forwardMessage(Socket socket, String fwdMsg) {
    
    

        InetAddress address = socket.getInetAddress();
        int port = socket.getPort();
        String key = address + ":" + port;

        this.connectedClients.keySet().forEach(id -> {
    
    
            if (!id.equals(key)) {
    
    
                Writer writer = connectedClients.get(id);
                try {
    
    
                    writer.write(fwdMsg);
                    writer.flush();
                } catch (IOException e) {
    
    
                    log.error("Exception: {}", e.getMessage());
                }
            }
        });
    }

    public boolean readyToQuit(String msg) {
    
    
        return QUIT.equalsIgnoreCase(msg);
    }

    public synchronized void close() {
    
    
        if (serverSocket != null) {
    
    
            try {
    
    
                serverSocket.close();
            } catch (IOException e) {
    
    
                log.error("Exception: {}", e.getMessage());
            }
        }
    }

    public void startServerApplication() {
    
    
        try {
    
    
            // 绑定监听窗口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            log.info("启动服务器,监听端口{} ...", DEFAULT_PORT);

            while (true) {
    
    
                // 等待客户端连接
                Socket socket = serverSocket.accept();
                // 创建ChatHandler线程, 处理每一个客户端的连接
                Thread thread = new Thread(new ChatHandler(this, socket));
                thread.setDaemon(true);
                thread.start();
            }
        } catch (IOException e) {
    
    
            log.error("Exception: {}", e.getMessage());
        } finally {
    
    
            this.close();
            log.info("服务器已关闭.");
        }
    }

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

package top.onefine.demo.socket.nio.server;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @author one fine<br/>
 */
@AllArgsConstructor
@Slf4j
public class ChatHandler implements Runnable {
    
    

    private ChatServer server;
    private Socket socket;

    @Override
    public void run() {
    
    
        try {
    
    
            // 存储新上线用户
            server.addClient(socket);
            // 读取用户发送的消息
            BufferedReader reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

            String msg;
            while ((msg = reader.readLine()) != null) {
    
    

                log.debug("客户端[{}:{}]发送消息: {}", socket.getInetAddress(), socket.getPort(), msg);

                String sendMsg = "[" + socket.getInetAddress() + ":" + socket.getPort() + "]" + msg + "\n";
                // 将收到的消息转发给聊天室中在线的其他用户
                server.forwardMessage(socket, sendMsg);

                // 退出群聊
                if (this.server.readyToQuit(msg))
                    break;
            }
        } catch (IOException e) {
    
    
            log.error("Exception: {}", e.getMessage());
        } finally {
    
    
            try {
    
    
                this.server.removeClient(socket);
            } catch (IOException e) {
    
    
                log.error("Exception: {}", e.getMessage());
            }
        }
    }
}

客户端实现:

package top.onefine.demo.socket.nio.client;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author one fine<br/>
 */
@Slf4j
public class ChatClient {
    
    

    private final String DEFAULT_SERVER_HOST = "127.0.0.1";
    private final int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";

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

    // 发送消息
    public void send(String msg) throws IOException {
    
    
        if (!socket.isOutputShutdown()) {
    
    
            writer.write(msg + "\n");
            writer.flush();
        }
    }

    // 接收消息
    public String receive() throws IOException {
    
    
        String msg = null;
        if (!socket.isInputShutdown()) {
    
    
            msg = reader.readLine();
        }
        return msg;
    }

    // 检查用户是否需要退出群聊
    public boolean readyToQuit(String msg) {
    
    
        return QUIT.equalsIgnoreCase(msg);
    }

    public void close() {
    
    
        if (writer != null) {
    
    
            try {
    
    
                writer.close();
            } catch (IOException e) {
    
    
                log.error("Exception: {}", e.getMessage());
            }
        }
    }

    public void startClientApplication() {
    
    
        try {
    
    
            // 创建socket
            socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_PORT);
            // 创建IO流
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            log.info("客户端[{}:{}]连接上服务器...", socket.getLocalAddress(), socket.getLocalPort());

            // 处理用户的输入
            Thread thread = new Thread(new UserInputHandler(this));
            thread.setDaemon(true);
            thread.start();

            // 读取服务器转发的消息
            String msg;
            while ((msg = receive()) != null) {
    
    
                log.info("收到消息: {}", msg);
            }

        } catch (IOException e) {
    
    
            log.error("Exception: {}", e.getMessage());
        } finally {
    
    
            this.close();
            log.info("服务器已关闭.");
        }
    }

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

package top.onefine.demo.socket.nio.client;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @author one fine<br/>
 */
@Slf4j
@AllArgsConstructor
public class UserInputHandler implements Runnable {
    
    

    private ChatClient chatClient;

    @Override
    public void run() {
    
    
        try {
    
    
            // 等待用户输入消息
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
    
    
                String input = consoleReader.readLine();

                // 向着服务器发送消息
                chatClient.send(input);

                // 检查用户是否准备退出
                if (chatClient.readyToQuit(input))
                    break;
            }
        } catch (IOException e) {
    
    
            log.error("Exception: {}", e.getMessage());

        }

    }
}

测试用户:

package top.onefine.demo.socket.nio;

import top.onefine.demo.socket.nio.client.ChatClient;

/**
 * @author one fine<br/>
 */
public class Client1 {
    
    
    public static void main(String[] args) {
    
    
        ChatClient client = new ChatClient();
        client.startClientApplication();
    }
}

package top.onefine.demo.socket.nio;

import top.onefine.demo.socket.nio.client.ChatClient;

/**
 * @author one fine<br/>
 */
public class Client2 {
    
    

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

在这里插入图片描述

代码实现(已更改部分):

package top.onefine.demo.socket.nio.server;

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
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;

/**
 * @author one fine<br/>
 */
@Slf4j
public class ChatServer {
    
    

    private final int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";
    private ServerSocket serverSocket;
    private final Map<String, 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) {
    
    
            InetAddress address = socket.getInetAddress();
            int port = socket.getPort();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            this.connectedClients.put(address + ":" + port, writer);
            log.info("客户端[{}:{}]成功连接到服务器...", address, port);
        }
    }

    public synchronized void removeClient(Socket socket) throws IOException {
    
    
        if (socket != null) {
    
    
            InetAddress address = socket.getInetAddress();
            int port = socket.getPort();
            String key = address + ":" + port;
            if (this.connectedClients.containsKey(key)) {
    
    
                Writer writer = connectedClients.get(key);
                writer.close();  // 关闭
                this.connectedClients.remove(key);
                log.info("客户端[{}:{}]断开与服务器连接.", address, port);
            }
        }
    }

    /**
     * 转发消息
     * @param socket 消息发送者
     * @param fwdMsg 需要转发的消息内容
     */
    public synchronized void forwardMessage(Socket socket, String fwdMsg) {
    
    

        InetAddress address = socket.getInetAddress();
        int port = socket.getPort();
        String key = address + ":" + port;

        this.connectedClients.keySet().forEach(id -> {
    
    
            if (!id.equals(key)) {
    
    
                Writer writer = connectedClients.get(id);
                try {
    
    
                    writer.write(fwdMsg);
                    writer.flush();
                } catch (IOException e) {
    
    
                    log.error("Exception: {}", e.getMessage());
                }
            }
        });
    }

    public boolean readyToQuit(String msg) {
    
    
        return QUIT.equalsIgnoreCase(msg);
    }

    public synchronized void close() {
    
    
        if (serverSocket != null) {
    
    
            try {
    
    
                serverSocket.close();
            } catch (IOException e) {
    
    
                log.error("Exception: {}", e.getMessage());
            }
        }
    }

    public void startServerApplication() {
    
    
        try {
    
    
            // 绑定监听窗口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            log.info("启动服务器,监听端口{} ...", DEFAULT_PORT);

            while (true) {
    
    
                // 等待客户端连接
                Socket socket = serverSocket.accept();
                // 创建ChatHandler线程, 处理每一个客户端的连接
//                Thread thread = new Thread(new ChatHandler(this, socket));
//                thread.setDaemon(true);
//                thread.start();
                // 使用线程池
                executorService.execute(new ChatHandler(this, socket));
            }
        } catch (IOException e) {
    
    
            log.error("Exception: {}", e.getMessage());
        } finally {
    
    
            this.close();
            log.info("服务器已关闭.");
        }
    }

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

猜你喜欢

转载自blog.csdn.net/jiduochou963/article/details/108171003