美文网首页编程
Netty学习(1):IO模型之BIO

Netty学习(1):IO模型之BIO

作者: 冰尘缘 | 来源:发表于2020-02-13 09:06 被阅读0次

    概述

    Netty其实就是一个异步的、基于事件驱动的框架,其作用是用来开发高性能、高可靠的IO程序。

    因此下面就让我们从Java的IO模型来逐步深入学习Netty。

    IO模型

    IO模型简单来说,就是采用如何的方式来进行数据的接受和发送,因为存在着发送方和接收方两个角色,因此IO模型一般分为以下3类:BIO(同步阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)。其3者的区别如下:

    • BIO:是最传统的Java IO模型,采用的方式为服务器新接受到一个连接,就建立一个线程,但是如果这个连接不做任何事情,该线程也会被创建,闲置着,增加不必要的开销。
    • NIO:服务器用一个线程处理多个请求,将所有请求注册到一个线程管理的多路复用器(Selector)上。适用于连接数量多,但连接比较轻量的服务上,如聊天,弹幕等。
    • AIO:其特点是由OS完成后,才通知服务端程序启动程序来处理,使用场景为相册服务器等。因不是很清楚具体实现,因此在这不展开了。

    BIO代码实现

    使用BIO模型来实现一个服务器端和客户端,并采用线程池的概念,使其可以连接多个客户端。

    服务端

    首先,让我们来看建立一个服务端需要哪些步骤:

    // 创建线程池,如果有连接,就创建一个线程  ps:这里手动指定参数创建线程池会更好,但这里因为不是重点,因此就采用默认的线程池来实现。
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            // create ServerSocket
            ServerSocket serverSocket = new ServerSocket(6666);
            System.out.println("服务端建立,监听!!!");
    
            while (true) {
                System.out.println("thread info0 id : " + Thread.currentThread().getId() + " \tname: " + Thread.currentThread().getName());
    
                // waiting for client
                System.out.println("等待客户端连接");
                // The method blocks until a connection is made.
                final Socket socket = serverSocket.accept();
                System.out.println("客户端连接成功");
    
                // create a thread for communicated
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        handler(socket);
                    }
                });
            }
    

    上述代码就是一个服务器端启动的代码实例,那么该如何处理客户端发送的数据呢。

        public static void handler(Socket socket) {
    
            try (InputStream inputStream = socket.getInputStream();
                 OutputStream outputStream = socket.getOutputStream();
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                 PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true)) {
    
                System.out.println("thread info1 id : " + Thread.currentThread().getId() + " \tname: " + Thread.currentThread().getName());
    
                String expression;
                String result;
                while (true) {
                    System.out.println("thread info2 id : " + Thread.currentThread().getId() + " \tname: " + Thread.currentThread().getName());
                    //BufferedReader.readLine()会读满缓冲区或者在读到文件末尾(遇到:"/r"、"/n"、"/r/n")才返回
                    System.out.println("等待读取数据");
                    if ((expression = bufferedReader.readLine()) == null) {
                        break;
                    }
                    System.out.println("result: " + expression);
                }
            } catch (IOException e) {
                System.out.println(e);
            } finally {
                System.out.println("关闭和client连接!!!");
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    

    在这里我们采用循环读取的形式来处理客户端发送的数据,并将其进行输出。

    客户端

     // 发送
            try (Socket socket = new Socket(InetAddress.getByName("localhost"), 6666);
                 OutputStream outputStream = socket.getOutputStream()) {
                String data = "0A,3,863921030884418,23.178061,0,113.225998,0,2020/01/18 17:28:30,051,4,FF,";
                data = getData(data) + "\n";
                outputStream.write(data.getBytes());
            } catch (Exception e) {
                System.out.println(e);
            }
    

    客户端的代码就很简单了,指定IP和端口,直接连接发送数据即可。

    实现效果

    首先我们启动服务端,可以看到如下输出,说明服务端已经被启动起来了,等待客户端连接。

    BIO服务端启动

    然后,我们启动客户端1,服务端会启动一个线程来接受并处理请求。

    BIO请求处理

    BIO实现问题

    上述就是一个BIO程序实现的Demo版本,这也就是BIO的优点所在,其实现简单,代码逻辑也十分的简洁明了。但其有几个很重要的问题没有解决。

    1. 对应于每个请求都要创建一个来解决,即使有线程池的存在,也会造成很大的线程资源损耗;
    2. handler() 代码中,我们可以看到其是在线程里处理的,但如果线程创建完成后,发现没有数据需要读写,那么其则会阻塞在 read 操作中,影响性能。

    正因为BIO有着这许多问题,因此Java官方在1.4后引入了NIO的概念,来完善Java的IO模型,因此,在这里我们只需要了解这种模型即可,知道其会产生什么问题以及产生的原因,接下里,我们把重点放在NIO上,而这也是Netty实现的基础。

    备注

    本文中代码已上传到GitHub上,地址为 https://github.com/wb1069003157/nettyPre-research ,欢迎大家来讨论,探讨。

    iceWang公众号

    文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

    相关文章

      网友评论

        本文标题:Netty学习(1):IO模型之BIO

        本文链接:https://www.haomeiwen.com/subject/wshkfhtx.html