epoll编程实例客户端_网络编程(一)BIO、NIO原理解析

本篇文章,笔者将和大家一起探讨BIO、NIO的原理。希望通过这边文章能让读者了解BIO、NIO他们的区别以及原理。本文涉及一些操作系统相关知识、笔者尽可能使用通俗的文字叙述。

BIO:同步阻塞IO NIO:同步非阻塞IO

上面文字笔者已经加粗、可以看出来这两行很重要啊。是的接下来我们就是从这两行开始分析。两行的唯一不同就是NIO多了一个非字。

Request 1: 那么BIO阻塞点在哪里呢?

下面我们看一段经典的BIO代码

  ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            Socket client = serverSocket.accept(); //堵塞点
            byte[] b = new byte[1024];
            InputStream in = client.getInputStream();
            in.read(b); //堵塞点
            in.close();
            client.close();
        } 

上面的代码以及注释说明了BIO的阻塞点会在accept和read的时候堵塞。当然我们可以有一些方案比如说用线程池去处理连接。但是即便用线程池去处理连接也只是即将accept这一步的堵塞给处理了。在我线程池处理连接的时候也会存在read的时候堵塞,这就导致了一种情况客户端还没有写数据,服务端一直在阻塞(当前线程资源也不能分配给其他新连接使用)。到这里相信大家应该知道了如果采用线程池策略一个client对应一个线程,并且在线程执行完成之前这个线程资源是不能被释放的。

假设当前线程数是10现在已经有10个客户端连接了,并且都在堵塞中那第11个客户端连接的时候获取不到线程资源就会导致超时或者拒绝处理。

上一个问题线程的堵塞点在哪里,以及会引发什么问题,我们已经解答了。接下来的问题是

Request 2: 既然有堵塞我们是不是可以通过某些方式让他们不堵塞呢?

对于这个问题。应该有读者会想到。既然是Read的时候堵塞那我每次连接上来我把连接放到一个List里面去,然后每次去循环List看看他们有没有准备好(单线程处理),如果准备好了就读取不是就不会有堵塞了。代码就变成了这样子了

private static ArrayList<Socket> list = new ArrayList();
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        handler();
        while (true) {
            Socket client = serverSocket.accept(); //堵塞点
            list.add(client);
        }
    }
    public static void handler(){
        while(true){
            if(list.size() == 0) continue;

            for(Socket socket:list){
                //...具体处理
            }
        }
    }

上面代码每次有连接会直接加到一个ArrayList里面去,然后handler方法里面会轮训。这样会存在一些问题就是如果我们100客户端只有一个准备好读了,并且正好它就在第100个的位置。这样我前面额99次循环都是没用的。现在引出一个新的问题有没有一种方式。

Request 3:让程序知道哪些客户端已经准备好了呢?(今天的主角终于要现身了)

上面我们要解决的问题就是当客户端准备好了之后通知去处理请求。这很明显是一个观察者模式。我们是不是可以把客户端的连接、读、写等等操作都当做是一个事件。把这些事件跟一个入口(能知道触发事件的东西)连接起来,当触发事件之后给我们返回一个事件列表,我们在根据事件的类型做不同的处理。

这里有一些基本的操作系统知识,所有的操作都是产生了CPU中断信号,然后CPU去做处理。对应上文就是连接、读、写、断连接等事件会触发CPU中断信号。有了这个中断信号我们不是就知道有事件了吗。有了事件就可以回调然后操作了。接下来我们来看一张图

2974f45d439255020b6a4cc4842dbe70.png

上图中Epoll会知道事件的触发、然后将触发的事件放到一个集合中去,应用程序获取事件信息进行响应处理就可以,如果还需要监听其他事件同样可以注册到Epoll中去。NIO就是基于Epoll实现了,将各类事件注册到epoll中去,如果有事件触发epollo会进行回调,在代码中就可以通过selector获取到事件了,然后进行处理。NIO可以通过单线程实现高并发。当然如果有需求我们可以根据具体的操作让线程池去处理。

到这里相信大家对于NIO已经很清楚了,对比BIO只是不再等待数据了而是有数据了通知程序去读取。

对于NIO的编程笔者这里就不把代码贴出来。读者可以根据对本文的理解然后对比NIO的代码就很容易理解NIO。

后话,NIO使用epoll其实是Reactor模型的一种实现。既然提到了epoll有些读者应该会想到select/poll模式。他们之间的区别就在与对事件列表筛选的方式。select/poll是要将所有事件列表遍历一遍,挑选出触发的事件列表,而epoll只选择有触发事件的事件列表。

文末我们提一下NIO中的Channel和Buffer。Channel是一个建立在内核和用户态之间的通道,应用程序可以直接通过Buffer到内核读取数据而不需要将数据从内核态拷贝到用户态。具体的在下一篇网络编程会分析

see you!

有兴趣可以关注微信公众号哈


d37561070306dfa2dcff52cbb7d3aeae.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值