IO那些事06-网络IO之IO角度下的NIO

NIO的语义

在java中,NIO意思是New IO
在操作系统中,NIO意思是非阻塞IO

代码实例

import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;

public class SocketNIO {

    //  what   why  how
    public static void main(String[] args) throws Exception {

        LinkedList<SocketChannel> clients = new LinkedList<>();



        ServerSocketChannel ss = ServerSocketChannel.open();  //服务端开启监听:接受客户端
        ss.bind(new InetSocketAddress(9090));
        ss.configureBlocking(false); //重点  OS  NONBLOCKING!!!  //只让接受客户端  不阻塞

//        ss.setOption(StandardSocketOptions.TCP_NODELAY, false);
//        StandardSocketOptions.TCP_NODELAY
//        StandardSocketOptions.SO_KEEPALIVE
//        StandardSocketOptions.SO_LINGER
//        StandardSocketOptions.SO_RCVBUF
//        StandardSocketOptions.SO_SNDBUF
//        StandardSocketOptions.SO_REUSEADDR




        while (true) {
            //接受客户端的连接
            Thread.sleep(1000);
            SocketChannel client = ss.accept(); //不会阻塞?  -1 NULL
            //accept  调用内核了:1,没有客户端连接进来,返回值?在BIO 的时候一直卡着,但是在NIO ,不卡着,返回-1,NULL
            //如果来客户端的连接,accept 返回的是这个客户端的fd  5,client  object
            //NONBLOCKING 就是代码能往下走了,只不过有不同的情况

            if (client == null) {
                System.out.println("null.....");
            } else {
                client.configureBlocking(false); //重点  socket(服务端的listen socket<连接请求三次握手后,往我这里扔,我去通过accept 得到  连接的socket>,连接socket<连接后的数据读写使用的> )
                int port = client.socket().getPort();
                System.out.println("client...port: " + port);
                clients.add(client);
            }

            ByteBuffer buffer = ByteBuffer.allocateDirect(4096);  //可以在堆里   堆外

            //遍历已经连接进来的客户端能不能读写数据
            for (SocketChannel c : clients) {   //串行化!!!!  多线程!!
                int num = c.read(buffer);  // >0  -1  0   //不会阻塞
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);

                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + " : " + b);
                    buffer.clear();
                }


            }
        }
    }

}

通过strace追踪上面的代码,观察与BIO的不同:
在这里插入图片描述

可以发现,非阻塞下的socket服务,面对accept,会快速返回结果,-1的话代表当前还没有连接,且不会阻塞。

NIO通信模型

在这里插入图片描述

关于创建socket文件描述符,绑定端口,监听端口的操作和BIO是一致的,但在非阻塞模式下,对于accept这种系统调用是非阻塞的。 而当accept到了一个连接socket时,针对这个socket的读操作,也可以通过设置非阻塞模式而不阻塞。
从而,NIO实现了,在一个单线程的情况下,既可以建立多个连接,也可以去处理每一个有响应的socket的连接。
单线程是与BIO本质上的区别。

使用NIO的模式,然后使用之前的client进行压测,看效果,可以发现很快,但当文件描述符开启一定数量后,会报错:
在这里插入图片描述

通过ulimit -a一个进程可以打开的最大文件描述符:
在这里插入图片描述

更改单进程打开文件描述符限制个数:
在这里插入图片描述

除了ulimit的每个进程的开辟的文件描述符限制,还有全局总共可以开辟的文件描述符个数。
内核级别文件描述符:
内核根据物理内存大小进行评估总共可以开辟的文件描述符个数
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

NIO的优势

相较于BIO而言,实现了通过1个或者几个线程来解决N个IO连接的处理

NIO依旧存在的问题

虽然是非阻塞的,但是瓶颈在对接受的socket读取上面,还是要无差别的对全部的socket遍历进行recv,而recv是一个系统调用操作,这意味着要进行复杂度为O(n)的系统调用,而这种无差别的调用其实很多是没有意义的,因为有的socket确实就是没有进入可读状态。

延伸

那其实也就意味着,如果能够确定哪些socket可读,然后只对那些可读的socket进行recv,就可以进一步提高效率,解决当前瓶颈了。 也就是后续的,多路复用时代!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值