一个简单的BIO通信程序

同步阻塞IO(简称BIO)是最传统的一种IO模型,即在读和写的过程中会发生阻塞现象。
我们编写一个简单的服务端和客户端程序,寻找一下同步阻塞I/O的弊端

服务端代码

TimeServer

public class TimeServer {

    public static void main(String[] args) throws IOException {
        ServerSocket server = null;
        try {
            // 如果端口未被占用且合法则创建成功
            server = new ServerSocket(8080);
            Socket socket = null;
            // 通过无限循环来监听客户端连接
            while (true) {
            	// ★ 1. 如果没有客户端接入,主线程阻塞在accept操作上
                socket = server.accept();
                // ★ 2. 一个客户端连接,启动一个线程进行处理
                new Thread(new TimeServerHandler(socket)).start();
            }
        } catch (Exception e) {
            System.out.println("创建serverSocket失败,端口:" + 8080);
            e.printStackTrace();
        } finally {
            System.out.println("The time server close");
            if (server != null) {
                server.close();
            }
        }
    }
}

服务端创建一个ServerSocket,然后通过无限循环的方式来监听客户端连接,如果没有客户端接入,则主线程会阻塞在accept操作上。当有客户端连接接入后,启动一个线程来处理,具体处理交给TimeServerHandler,这是个Runnable,使用它为构造函数的参数创建一个新的客户端线程处理这条Socket链路。
TimeServerHandler

public class TimeServerHandler implements Runnable {

    private Socket socket;

    public TimeServerHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String currentTime;
            String body;
            while (true) {
                // ★ 读取一行,如果读到输入流尾部,则返回值为null,退出循环
                body = in.readLine();
                if (null == body) {
                    break;
                }
                System.out.println("The time server receive order : " + body);
                // 如果读到了非空值,对内容进行判断,如果请求消息为查询时间的指令 QUERY TIME ORDER,则获取当前系统时间
                // 通过PrintWriter的println函数发送给客户端,然后退出循环
                if ("QUERY TIME ORDER".equalsIgnoreCase(body)) {
                    currentTime = new Date(System.currentTimeMillis()).toString();
                } else {
                    currentTime = "BAD ORDER";
                }
                // ★ 通过printWriter发送给客户端
                out.println(currentTime);
            }
        } catch (IOException e) {
            System.out.println("exception");
            e.printStackTrace();
            // 释放输入流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            // 释放输出流
            if (out != null) {
                out.close();
            }
            // 释放socket套接字句柄资源
            if (this.socket != null) {
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}

TimeServerHandler通过Socket读取输入流,每次读一行,如果读到了尾部,则返回null,退出循环。如果读到了非空值,对非空值进行判断,如果请求的消息为查询时间的指令,则获取当前最新的世界,通过PrintWriter的println函数发送给客户端,最后退出循环。

客户端代码

TimeClient

public class TimeClient {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            //
            socket = new Socket("127.0.0.1", 8080);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
            // ★ 通过PrintWriter向服务端发送 QUERY TIME ORDER 指令
            out.println("QUERY TIME ORDER");
            System.out.println("send order to server succeed.");
            // ★ 通过BufferedReader读取响应并打印
            String response = in.readLine();
            System.out.println("Now is : " + response);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放输入流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            // 释放输出流
            if (out != null) {
                out.close();
            }
            // 释放socket套接字句柄资源
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}

通过PrintWriter向服务端发送指令,然后通过BufferReader的readLine读取响应内容。

分别执行服务端和客户端,执行结果如下:
服务端执行结果:

The time server receive order : QUERY TIME ORDER

客户端执行结果:

send order to server succeed.
Now is : Sat Jul 18 23:19:43 CST 2020

到此同步阻塞IO的示例程序讲解完毕

Talk is cheap , show me the picture
最后通过一张图来说明BIO的通信模型
在这里插入图片描述

总结

我们发现,BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理客户端,一个线程只能处理一个客户端连接。在成千上万个客户端并发连接的情况下,这种模型无法满足高性能、高并发接入的场景。

参考资料

  • 《Netty权威指南》第二版 by 李林锋
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值