一起来学Netty吧——开篇——BIO和NIO

Netty是什么


它是基于NIO的再度封装,修复了JDK 原有NIO的诟病,而近乎完美的网络通信框架。

那什么是NIO?

关于这个问题,我们可以先从 Java 最原始的 BIO说起。

首先强调一下,对 Java基本网络编程 的理解很重要,因为这是一层套一层的关系。如图:

一起来学Netty吧——开篇——BIO和NIO_第1张图片

这是个逐层封装和优化的过程。其中的核心主体一直都是网络。

我们这里先重点说一下BIO,也就是我们的传统Java网络编程。

Java网络编程(BIO)


我们首先给出结论,稍后我们代码验证。


如图:
一起来学Netty吧——开篇——BIO和NIO_第2张图片

当有一个客户端向服务器发出请求时,服务器便创建一个线程与之通信。

这种策略显然有个很大的限制,就是无法招架高并发,,

当并发请求多了,服务器就招架不住了,,可能回绝后续的请求,也可能崩溃。

Code


服务器端:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings("all")
public class BIOServer {
    public static void main(String[] args) throws IOException {
        //线程池机制

        /** 思路:
         *  1.创建一个线程池
         *  2.如果有客户端接入,创建一个线程与之通信
         */
        //线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();

        ServerSocket server = new ServerSocket(6666);
        System.out.println("服务启动了");
        while (true){
            //创建一个Socket
            final Socket socket = server.accept();
            System.out.println("接入一个客户端——线程信息:"+Thread.currentThread().getName());
            //创建一下线程与之通信
            threadPool.execute(()->{
                handler(socket);
            });
        }
    }

    public static void handler(Socket socket) {

        try {
            InputStream inputStream = socket.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            int len;
            while (true){
                len = inputStream.read(bytes);
                if (len != 0){
                    baos.write(bytes,0,len);
                    System.out.println("线程信息:" + Thread.currentThread().getName()+":“"+baos.toString()+"”");
                }
                else break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭连接——对应线程:"+Thread.currentThread().getName());
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端我们采用Windows的Telnet:
一起来学Netty吧——开篇——BIO和NIO_第3张图片
验证流程:
一起来学Netty吧——开篇——BIO和NIO_第4张图片

服务器跑起来,但他在被阻塞着,因为他在等一个请求。

当我们执行完 “telnet 127.0.0.1 6666” 这句命令时,,
一起来学Netty吧——开篇——BIO和NIO_第5张图片

由主线程为我们负责接入客户端,然后线程又阻塞了,因为他在等待客户端发消息来。

一起来学Netty吧——开篇——BIO和NIO_第6张图片
服务器端打印结果:(收到了来自客户端的消息,并开始等待下次消息)

一起来学Netty吧——开篇——BIO和NIO_第7张图片

此时负责与该客户端通信的线程是 pool-1-thread-1

接下来我们在来一个客户端:

再开一个cmd窗口,并执行 telnet 127.0.0.1 6666 这条命令。

服务器打印结果:
一起来学Netty吧——开篇——BIO和NIO_第8张图片
可见,主线程又接入了一个客户端。


我们尝试通讯:

一起来学Netty吧——开篇——BIO和NIO_第9张图片
服务器端打印结果:
一起来学Netty吧——开篇——BIO和NIO_第10张图片

可见,这次负责和第二个客户端通信的线程为:pool-1-thread-2 。

由此得出,BIO 确实是每个客户端对应给出一个线程。

马师傅:这样好吗? 这样不好。。

很显然,这样确实不好。局限性太大。

BIO是在JDK1.4之前唯一的方法,从JDK1.4之后 开始支持 NIO。

NIO


NIO 是技术的发展,所以 他本质上就是优化 BIO。

先上图:
一起来学Netty吧——开篇——BIO和NIO_第11张图片

在听我解释:

之前的BIO是一个客户端对应一个线程,会有一个比较严重的问题:

  • 当客户端和服务器之间没有通信时,这个线程就会在这闲着。(这无疑是浪费)

所以有了NIO,来降低资源的闲置率。

上述模型主要阐述的思想就是,一个线程对一个客户端可能会有较多的时间处于闲置状态,所以我们就让一个线程多负责几个客户端,加一个 选择器(selector)轮询 他下面的客户端请求,谁有请求数据就让线程服务谁。

这无疑大大提高了资源利用率,并且也大大增加了并发量。

最后总结一下 BIO 和 NIO 的特点:

  • Java BIO : 同步阻塞,服务器实现模式为一个客户端一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

    阻塞:客户端会等着请求完成才能去做其他事。

  • Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器(selector)上,多路复用器(selector)轮询到连接有I/O请求时才让线程进行处理。

    非阻塞:客户端不用等着请求完成 ,期间可以去其他事,但要时不时的去询问一下服务器的请求执行情况。

That’s all,,Thank you! !!

一起来学Netty吧——开篇——BIO和NIO_第12张图片

你可能感兴趣的:(Netty,netty,网络,java)