NIO之NIO介绍

2 篇文章 0 订阅

BIO

    在介绍NIO之前,我们来了解一下BIO以及为什么会出现NIO。下面来见一段传统的BIO形式的代码。

Server

        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(9876));

        while(true) {
            System.out.println("等待连接");
            //阻塞接收
            //程序释放CPU资源
            Socket accept = server.accept(); //mark1
            System.out.println("连接成功");

            //读数据
            byte[] buff = new byte[1024];
            int pos = accept.getInputStream().read(buff); //mark2
            String content = new String(buff);
            System.out.println("读出数据:" + content);

            //写数据
            accept.getOutputStream().write("服务器返回数据".getBytes());
            System.out.println("数据返回成功");
        }

Client

		Socket socket = new Socket();
        socket.connect(new InetSocketAddress("127.0.0.1", 9876));
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入内容");
        while(true) {
            String next = sc.next();
            socket.getOutputStream().write(next.getBytes());
        }

    代码很简单,非常常见的服务端客户端通信。那么在这段代码中,服务端有两个地方会阻塞,分别为mark1和mark2所标记的代码处。
    运行Server,程序阻塞在mark1处等待客户连接,此时启动Client,Server端将运行至mark2处阻塞等待Client传输数据,Client传输数据后,Server结束一次while循环。这是我们最开始传统的运行方式,这样的形式我Server端同时只能处理一个Client的请求(因为mark1处被阻塞了),如果想处理多个在BIO的模式下只能开多线程处理。
    BIO的模式下多线程能同时处理多个客户端请求吗,答案是肯定的。但是会有什么问题:

  • 多线程上下文切换的消耗
  • 服务器开线程数量的限制

    很明显会带来上述几个问题,那么我们能不能用单线程来处理多个客户端呢?单线程同时处理多个客户端请求我们要处理什么问题?

NIO

    能否以单线程处理多个客户端呢?答案是肯定的,我们只要处理两个问题,第一,将上述案例中的mark1变为非阻塞,第二,将mark2变为非阻塞形式,来看一段伪代码。

        Set<Socket> set = new HashSet<>(); 
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(9876));

        while(true) {
			setServerSocketNoBlock(server); //假设存在使得ServerSocket变为非阻塞的方法
            Socket accept = server.accept();
			if accept == null { //handle1
				//是否有客户端发送数据
				if yes {
					for set {
						accept.read(buff);
					}
				}
							
				//continue
			} else { //handle2
				setSocketNoBlock(accept); //假设存在使得Socket变为非阻塞的方法
				set.add(accept);
				for set {
					accept.read(buff);
				}
						
				//continue
			}          
        }

    在上述代码中我们将原来的mark1和mark2都变为非阻塞的形式,这样回发生什么,我们分析一下。Server端进入while循环,此时没有客户端连接accept 为null,没有客户端发送数据,程序进入handle1后进入下一次循环。一旦有客户端连接上来,假设此时A客户端连接上来但是不发送数据,程序进入handle2处理后进入下一次循环,此时B客户端连接上来,程序进入handle2后进入下一次循环,此时A客户端发送了一条数据消息,程序进入handle1并且接收到了A客户端的消息之后进入下一次循环。这样的形式就实现了我们单线程来处理多个客户端的需求。
    那么上述伪代码中的最重要的地方其实是两处非阻塞的代码,从JDK1.4开始,jdk官方给我们提供了ServerSocketChannel以及SocketChannel这两个类来代替我们原来的ServerSocket和Socket,查看其API可以发现configureBlocking函数,看其注释可知,这个方法就能实现非阻塞的模式,那么我们上述的伪代码即可实现。NIO大致原理就是这样,其中NIO还有几个比较重要的概念buff、channel和selector,我们后续博客会简单分析一下。

    /**
     * Adjusts this channel's blocking mode.
     *
     * <p> If the given blocking mode is different from the current blocking
     * mode then this method invokes the {@link #implConfigureBlocking
     * implConfigureBlocking} method, while holding the appropriate locks, in
     * order to change the mode.  </p>
     */
    public final SelectableChannel configureBlocking(boolean block)
        throws IOException
    {
        synchronized (regLock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if (blocking == block)
                return this;
            if (block && haveValidKeys())
                throw new IllegalBlockingModeException();
            implConfigureBlocking(block);
            blocking = block;
        }
        return this;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值