读完本篇文章将会了解以下问题

1. BIO模型概述

2. NIO模型概述

3. AIO模型概述

4. Netty

5.同步、异步、阻塞、非阻塞

---------------------------------------------------------------------------------------------------------------------------

1、BIO(Blocking IO)模型概述

        阻塞IO网络模型:服务器启动后会进入阻塞状态,等待client连接,每一个client端连接上服务器后,服务器会为每一个客户端起一个线程来处理客户端的需求。服务器的accept()方法、服务器新起的thread中,Socket的read()和write()方法都是阻塞的。

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_非阻塞

BIO为什么要为每一个客户端起一个新线程处理,用一个线程不行么?

        不行,因为如果server用一个线程来处理的话,假设当前client端传输一个特别大的文件,服务器端一直进入阻塞状态,等待文件传输结束,会导致其他client端无法连接到服务器。

2、NIO(Non-Blocking IO)模型概述

2.1、NIO-Single Thread模型

        NIO单线程模型:采用selector管理的轮询查询模式,selector每隔一段时间都去看一下client端有没有产生需要处理的消息(客户端连接请求、客户端发送数据请求、客户端下载数据请求),也就是说selector会同时管理client端的连接和通道内client端的读、写请求。

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_服务器_02

public class Server{

    public static void main(String[] args) {
        
        ServerSocketChannel ssc = ServerSocketChannel.open(); //ServerSocketChannel可支持同时读写
        ssc.socket().bind(new InetSocketAddress("127.0.0.1",8888));
        ssc.configureBlocking(false);//设定为非阻塞模型
        
        Selector selector = Selector.open();//开启Selector
        ssc.register(selector,SelectionKey.OP_ACCEPT);//注册对client端连接感兴趣
        
        while(true){
            selector.select();//等待有事件发生
            Set<SelectionKey> Keys = selector.selectedKeys();
            Iterator<SelectionKey> it =keys.iterator();
            while(it.hasNext()){
                SelectionKey key = it.next();
                it.remove();
                handle(key);//
            }
        }
    }
}

在NIO单线程模型中有以下几点和BIO不同:

  1.        BIO为阻塞模型,NIO为非阻塞模型
  2.        BIO通道为Socket,是单向的;NIO为ServerSocketChannel,是双向的。
  3.        BIO没有selector,NIO设计了selector,为每一个client注册一个key,用来唯一表示一个通道,同时采用注册模式为通道添加功能。
  4.        BIO为每一个client端启用一个线程,NIO采用轮询模式处理client端的连接或数据读写请求。

2.2、NIO-reactor模式(响应式编程模式)

        在NIO单线程模式中,只有一个线程负责处理client端的连接和通道内client端的读、写请求。在reactor模式中,将server端的单线程换成线程池(单线程管家+线程池工人模式)。

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_非阻塞_03

3、AIO(Asynchronous IO)模型概述

        AIO模型不再需要轮询,每当有client端发出连接服务器请求的时候,由操作系统通知selector(大管家)去处理连接请求,selector为client和工人线程池中的一个线程创建通道。

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_非阻塞_04

4、Netty

        Netty实现了对NIO、NIO(很少用)的封装,封装成了AIO API的样子。代码较NIO通俗易懂且好用,代码样例:

public class SimpleChatServer {
    private int port;

    public SimpleChatServer(int port){
        this.port = port;
    }

    public void run() throws Exception{
    	
        EventLoopGroup bossGroup = new NioEventLoopGroup();//bossGroup用来接收进来的连接  parent接收者
        EventLoopGroup workerGroup = new NioEventLoopGroup();//workerGroup用来处理已经被接收的连接  child客户端:WorkGroup
        
        try{
            ServerBootstrap sBootstrap = new ServerBootstrap(); //是一个启动NIO服务的辅助启动类
            sBootstrap.group(bossGroup, workerGroup)//初始化线程池
                    .channel(NioServerSocketChannel.class)//指定通道channel的类型 NioServerSocketChannel:客户端
                    .childHandler(new SimpleChatServerInitializer())//设置自己编写的处理器
                    .option(ChannelOption.SO_BACKLOG, 128)//设置通道的选项参数
                    .childOption(ChannelOption.SO_KEEPALIVE, true);//设置通道的选项参数
            System.out.println("远程认证服务器:Server start");
            ChannelFuture future = sBootstrap.bind(port).sync();//绑定端口,开启连接
            future.channel().closeFuture().sync();//等待服务器socket关闭
        } finally {
            workerGroup.shutdownGracefully();//Netty优雅退出机制,使线程池退出。
            bossGroup.shutdownGracefully();//Netty优雅退出机制,使线程池退出。
            System.out.println("远程认证服务器:Server end");
        }
    }

    public static void main(String[] args) throws Exception {
        new SimpleChatServer(8080).run();
    }
}
public class SimpleChatClient {
	private final int port;
	private final String host;

	public SimpleChatClient(String host, int port) {
		this.host = host;
		this.port = port;
	}
	
	public void run(String username,String password) throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();//用来处理IO操作的多线程事件循环器
		try {
			Bootstrap bootstrap = new Bootstrap();// 是一个启动NIO服务的辅助启动类
			bootstrap.group(group).channel(NioSocketChannel.class)//创建连接通道,加入线程池
					.handler(new SimpleChatClientInitializer());
			Channel channel = bootstrap.connect(host, port).sync().channel();//调用连接,返回通道
			System.out.println("=============================================================================");
			System.out.println("远程认证客户端:已创建远程认证客户端线程");
			channel.writeAndFlush("username|"+username+"|password|"+password+"\r\n");//输入数据并将数据推栈
			System.out.println("QQ远程认证客户端:发送至服务器的消息为 username="+username+" AND password="+password);
			Thread.sleep(1500);//睡眠1500ms保证CSDN客户端响应完毕
			System.out.println("远程认证客户端:已销毁当前认证客户端线程");
			group.shutdownGracefully();//Netty优雅退出机制,使线程池退出。	
		
		} catch (Exception e) {
			e.printStackTrace();//捕获并输出异常	
		}
	}
}
5、同步、异步、阻塞、非阻塞

       对同一件事的观点不同,有时候可能表现为同步异步、也可能表现为阻塞非阻塞。

       同步异步关注的是消息通信机制

       阻塞非阻塞关注的是等待消息时的状态

什么意思呢?例:

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_服务器_05

       有一个人和一个水壶,这个人首先点火(发消息),点完火后搬个板凳坐在旁边等着,还是同一个人处理水是否烧开这条消息(同步),不等到水开不去做别的事情(阻塞),这就是同步阻塞

       还是这个人,还是这个壶。点火(发消息),点完火这个人去打游戏了,每隔五分钟来看一眼水是否烧开(非阻塞),烧水期间还是由同一个人处理水是否烧开这条消息(同步),这就是同步非阻塞

Java的IO模型与Netty概述(BIO/NIO/AIO/Netty)_bootstrap_06

       

       还是这个人,这个壶,这次不一样的是,这个人给壶进行了改装,烧开后壶会自动触发响铃处理。开始点火(发消息),烧水期间这个人一直盯着这个铃当(阻塞),水烧开后触发响铃操作,水是否烧开这条消息不是由这个人处理的,而是由铃铛处理的(异步),这就是异步阻塞

       这个人,这个壶,点火(发消息),烧水期间这个人该干嘛就干嘛(非阻塞),水是否烧开这条消息由铃铛处理(异步),这就是异步非阻塞