Java BIO多人聊天室

基于上篇NIO的多人聊天室,这篇将用BIO也实现一遍

首先是服务端的设计:
/**
 * @author Jing
 * @create 2020/5/17
 */
public class ChatServer {

    private int DEFAULT_PORT = 8888;
    private final String Quit = "quit";

    private ServerSocket serverSocket;

    private Map<Integer, Writer> connectedClients; // 这里用map保存端口,和writer的映射

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

    public synchronized void addClient(Socket socket) throws IOException {
        if(socket != null){
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            connectedClients.put(socket.getPort(),writer);
            System.out.println("客户端:【"+socket.getPort()+"】已经连接到服务器了");
        }
    }

    public synchronized void removeClient(Socket socket) throws IOException {
        if(socket != null){
            if(connectedClients.containsKey(socket.getPort())){
                connectedClients.get(socket.getPort()).close();
                connectedClients.remove(socket.getPort());
            }
        }
        System.out.println("客户端: "+socket.getPort()+"已经断开了连接");
    }

    public synchronized void broadCast(Socket socket,String msg) throws IOException { // 服务器轮流发送信息给所有的客户端
        for(Integer id : connectedClients.keySet()){
            if(id != socket.getPort()){
                Writer writer = connectedClients.get(id);
                writer.write(msg);
                writer.flush();
            }
        }
    }

    public synchronized 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("启动服务器,监听端口 :"+DEFAULT_PORT);
            while(true){
                Socket socket = serverSocket.accept();
                // 创建chatHandler线程
                new Thread(new ChatHandler(this,socket)).start();

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close();
        }

    }
    public boolean checkToQuit(String msg){
        return msg.equals(Quit);
    }

    public static void main(String[] args) {
        new ChatServer().start();
    }
}
下面这个线程专门用来读取各个客户端发送过来的消息,对消息进行广播
public class ChatHandler implements Runnable {

    private ChatServer chatServer;
    private Socket socket;

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


    @Override
    public void run() {
        try {
            chatServer.addClient(socket);
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            String msg = null;
            while((msg = bufferedReader.readLine()) != null){
                String content = "客户端【"+socket.getPort()+"】:"+msg+"\n";
                System.out.println(content);
                chatServer.broadCast(socket,content);
                if(chatServer.checkToQuit(msg)){
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                chatServer.removeClient(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


以上是服务端的代码,现在编写客户端的代码

public class ChatClient {

    private final int DEFAULT_PORT = 8888;
    private final String Quit = "quit";
    private final String DEFAULT_HOST = "127.0.0.1";

    private Socket socket;
    private BufferedReader bufferedReader;
    private BufferedWriter bufferedWriter;

    public ChatClient(){}

    public void send(String msg) throws IOException {
        if(!socket.isOutputShutdown()){
            bufferedWriter.write(msg+"\n");
            bufferedWriter.flush();
        }
    }

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

    // 检查用户退出
    public boolean isQuit(String msg){
        return Quit.equals(msg);
    }

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

    public void start(){  
        try {
            socket = new Socket(DEFAULT_HOST,DEFAULT_PORT);
            bufferedWriter = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            bufferedReader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            // 这里另开一个线程用于处理用户的输入
            new Thread(new UserInputHandler(this,socket)).start();
            // 读取服务器转发的消息
            String msg = null;
            while((msg = receive())!= null){
                System.out.println(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close();
        }
    }

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

}

主线程用来接收消息,另外开一个线程专门处理客户的输入:

public class UserInputHandler implements Runnable{

    private ChatClient chatClient;

    public UserInputHandler(ChatClient chatClient, Socket socket){
        this.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.isQuit(input)) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


然后,我们可以看一下,执行的效果,我开了两个客户端:

这是服务端:
在这里插入图片描述
这是客户端1:
在这里插入图片描述
这是客户端2:
在这里插入图片描述


对服务端进一步改进,加入线程池(主要对服务端三个部分进行进一步改进,其他的地方依旧不变):

private ExecutorService executorService;
public ChatServer(){
        connectedClients = new HashMap<>();
        executorService = Executors.newFixedThreadPool(10);
}
public void start(){

        try {
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("启动服务器,监听端口 :"+DEFAULT_PORT);
            while(true){
                Socket socket = serverSocket.accept();
                // 创建chatHandler线程
                executorService.execute(new ChatHandler(this,socket));

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close();
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章