BIO学习

匆匆过客 提交于 2020-07-27 08:22:18

1. BIO介绍

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

2. 代码介绍BIO

2.1 简单通信

实现一个简单功能,服务器端启动之后等待客户端连接,连接之后互相通信就结束

2.1.1 服务器端

 public class Server {
    public static void main(String[] args) throws IOException {
        //服务器端绑定888端口
        ServerSocket ss = new ServerSocket(888);
        //这里一直循环是一直可以等待处理客户端请求,可以处理多个客户端请求,但是一次只能处理一个
        while(true) {
            //阻塞方法,一直等待客户端接入
            Socket s = ss.accept();
            //客户端接入后就开启一个线程与客户端通信
            new Thread(() -> {
                handle(s);
            }).start();
        }
    }
    static void handle(Socket s) {
        try {
            byte[] bytes = new byte[1024];
            int len = s.getInputStream().read(bytes);
            System.out.println(new String(bytes, 0, len));
            s.getOutputStream().write(bytes, 0, len);
            s.getOutputStream().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.1.2 客户端

public class Client {
    public static void main(String[] args) throws IOException {
        //和服务端建立连接
        Socket s = new Socket("127.0.0.1", 888);
        //向服务端发送一条信息
        s.getOutputStream().write("HelloServer".getBytes());
        s.getOutputStream().flush();

        System.out.println("write over, waiting for msg back...");
        byte[] bytes = new byte[1024];
        //读取服务器端消息
        int len = s.getInputStream().read(bytes);
        System.out.println(new String(bytes, 0, len));
        s.close();
    }
}

2.2 两个用户通信

实现功能:服务端和客户端可以互相通信

2.2.1 服务端

public class Server {
    static ExecutorService executor = Executors.newFixedThreadPool(2);
    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(888);
        Socket socket = serverSocket.accept();

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        //开启一个线程,专门用于读取数据
        executor.submit(()->{
            try{
                String line = null;
                while (true){
                    line = reader.readLine();
                    System.out.println(line);
                }
            }catch (Exception e){}
        });
        //开启一个线程,专门用于从键盘读入数据之后通信
        executor.submit(()->{
            while (true){
                try{
                    BufferedReader consoleReader = new BufferedReader( new InputStreamReader(System.in));
                    String input = consoleReader.readLine();
                    writer.write(input + "\n");
                    writer.flush();
                }catch (Exception e){}
            }
        });
        while(true){}
    }
}
2.2.2 客户端
public class Client {
    static ExecutorService executor = Executors.newFixedThreadPool(2);
    public static void main(String[] args) throws Exception{
        Socket socket = new Socket("127.0.0.1",888);

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        //开启一个线程,专门用于读取数据
        executor.submit(()->{
            try{
                String line = null;
                while (true){
                    line = reader.readLine();
                    System.out.println(line);
                }
            }catch (Exception e){}
        });
        //开启一个线程,专门用于从键盘读入数据之后通信
        executor.submit(()->{
            while (true){
                try{
                    BufferedReader consoleReader = new BufferedReader( new InputStreamReader(System.in));
                    String input = consoleReader.readLine();
                    writer.write(input + "\n");
                    writer.flush();
                }catch (Exception e){}
            }
        });

        while(true){}
    }
}

2.3 多人通信

服务器端用于接收客户端数据并转发到其他客户端

2.3.1 服务器端

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

    private ServerSocket serverSocket;

    /**
     * 保存连接的客户端信息
     */
    private Map<Integer, Writer> connectedClients;

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

    /**
     * 添加客户端
     *
     * @param socket
     */
    public synchronized void addClient(Socket socket) throws IOException {
        if (socket != null) {
            int port = socket.getPort();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            connectedClients.put(port, writer);
            System.out.println("客户端[" + port + "]已连接到服务器");
        }
    }

    /**
     * 移除客户端
     *
     * @param socket
     * @throws IOException
     */
    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 + "]已断开连接");
        }
    }

    /**
     * 转发消息
     *
     * @param socket
     * @param fwdMsg
     */
    public synchronized void forwardMessage(Socket socket, String fwdMsg) throws IOException {
        for (Integer id : connectedClients.keySet()) {
            // 排除发送者本人
            if (!id.equals(socket.getPort())) {
                Writer writer = connectedClients.get(id);
                writer.write(fwdMsg);
                writer.flush();
            }
        }
    }

    /**
     * 监听是否准备退出标志
     *
     * @param msg
     * @return
     */
    public boolean readyToQuit(String msg) {
        return QUIT.equals(msg);
    }

    /**
     * 关闭serverSocket
     */
    public synchronized void close() {
        if (serverSocket != null) {
            try {
                serverSocket.close();
                System.out.println("关闭serverSocket");
            } 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 static void main(String[] args) {
        ChatServer server = new ChatServer();
        server.start();
    }
}
public class ChatHandler implements Runnable {

    private ChatServer server;
    private Socket socket;

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

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

            String msg = null;
            while ((msg = reader.readLine()) != null) {
                String fwdMsg = "客户端[" + socket.getPort() + "]:" + msg + "\n";
                System.out.println(fwdMsg);
                // 将消息转发给聊天室里在线的其他用户
                server.forwardMessage(socket, fwdMsg);
                // 检查用户是否准备退出
                if (server.readyToQuit(msg)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                server.removeClient(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.3.2 客户端

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

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

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

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

    /**
     * 检查用户是否准备退出
     *
     * @param msg
     * @return
     */
    public boolean readyToQuit(String msg) {
        return QUIT.equals(msg);
    }

    /**
     * 关闭socket
     */
    public void close() {
        if (writer != null) {
            try {
                System.out.println("关闭socket");
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public void start() {
        try {
            // 创建socket
            socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);

            // 创建io流
            reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream())
            );
            writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );

            // 处理用户的输入
            new Thread(new UserInputHandler(this)).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) {
        ChatClient chatClient = new ChatClient();
        chatClient.start();
    }
}
public class UserInputHandler implements Runnable {
    private ChatClient chatClient;

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

    @Override
    public void run() {
        BufferedReader consoleReader = new BufferedReader(
                new InputStreamReader(System.in)
        );
        while (true) {
            try {
                String input = consoleReader.readLine();
                // 向服务器发送消息
                chatClient.send(input);
                // 检查用户是否准备退出
                if (chatClient.readyToQuit(input)) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!