BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程
BIO: 同步阻塞
通过TCP三次握手进行连接, 一个client对应一个server 通过一个线程进行处理 在linux中最大能同时处理2000个请求(一般情况下)
客户端Socket ---------- 服务端ServerSocket
BIO模型图(图源网络,原出处不明):
NIO:同步非阻塞
NIO的本质就是避免原始的TCP建立连接使用3次握手的操作,减少连接的开销
主要的有Buffer(缓冲区)、Channel(通道)、Selector(多路复用器)
Channel包含4个状态 连接(OP_CONNECT)、可读(OP_READ)、可写(OP_WRITE)、阻塞(OP_ACCEPT)
该模型为 将多个channel注册到一个Selector上 然后通过一个线程对 Selector进行轮询 处理 Channel上的的状态
NIO 第一次连接时也是用的TCP连接
1. Buffer(ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBUffer、FloatBuffer、DoubleBUffer)
2. 客户端SocketChannel---------服务端serverSocketChannel
3. Selector
伪异步IO模型图(图源网络,原出处不明):
创建NIO服务器的主要步骤
(1)打开ServerSocketChannel,监听客户端连接
(2)绑定监听端口,设置连接为非阻塞模式
(3)创建Reactor线程,创建多路复用器并启动线程
(4)将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件
(5)Selector轮询准备就绪的key
(6)Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,简历物理链路
(7)设置客户端链路为非阻塞模式
(8)将新接入的客户端连接注册到Reactor线程的Selector上,监听读操作,读取客户端发送的网络消息
(9)异步读取客户端消息到缓冲区
(10)对Buffer编解码,处理半包消息,将解码成功的消息封装成Task
(11)将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端
NIO 原理图:
代码启动时要先启动 Server端 再启动Client端
代码 Client端:
public class NioClient {
private final static String ip = "localhost";
private final static int port = 8081;
private Selector selector;
private void innit() throws IOException {
//1. 首先创建一个客户端的 socket 通道
SocketChannel channel = SocketChannel.open();
// 设置通道为非阻塞 TODO
channel.configureBlocking(false);
// 打开一个 多路复用器(通道管理器)
selector = Selector.open();
// 连接通道
channel.connect(new InetSocketAddress(ip, port));
// 将channel 注册到通道管理器上 并且设置当前通道的key为 连接
channel.register(selector, SelectionKey.OP_CONNECT);
}
private void listen() throws IOException {
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 删除已选的key,以防重复处理
iterator.remove();
// 如果是连接的时候
if (key.isConnectable()) {
// 获取当前的channel
SocketChannel socketChannel = (SocketChannel) key.channel();
// 如果是正在连接 则进行连接
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
// 设置成非阻塞 TODO
socketChannel.configureBlocking(false);
Scanner scanner = new Scanner(System.in);
System.out.println("ScannerTest, Please Enter Name:");
String name = scanner.nextLine();
socketChannel.write(ByteBuffer.wrap(("To Server:" + name).getBytes()));
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
// 进行读取的操作
}
}
}
}
public static void main(String[] args) throws IOException {
NioClient nioClient = new NioClient();
nioClient.innit();
nioClient.listen();
}
}
Server端:
public class NioServer {
private final static int port = 8081;
private Selector selector;
private void init(int port) {
try {
// 开启服务端的 socket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverSocketChannel.configureBlocking(false);
// 将该通道绑定到端口上
serverSocketChannel.socket().bind(new InetSocketAddress(port));
// 开启多路复用器
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
private void listen() throws IOException {
System.out.println("服务端启动成功");
while (true) {
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 防止重复处理
iterator.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
System.out.println("进入客户端连接····");
// 获取的是server端口的 通道
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
// 获取和客户端的链接 TODO
SocketChannel socketChannel = serverSocketChannel.accept();
// 设置成非阻塞
socketChannel.configureBlocking(false);
// socketChannel.write(ByteBuffer.wrap(new String("To Client:22222222").getBytes()));
// 将获取到客户端的通道注册到 多路复用器上
socketChannel.register(selector,SelectionKey.OP_READ);
}
if (key.isReadable()) {
read(key);
}
}
}
}
private void read(SelectionKey key) throws IOException {
// 服务器可读取消息:得到事件发生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 创建buffer 缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
byte[] array = buffer.array();
System.out.println("服务端收取到消息" + new String(array));
}
public static void main(String[] args) throws IOException {
NioServer nioServer = new NioServer();
nioServer.init(port);
nioServer.listen();
}
}
AIO: 异步非阻塞
在java1.7后才实现了 异步非阻塞
AIO编程,在NIO基础之上引入了异步通道的概念。并提供异步文件和异步套接字通道的实现,从而在真正意义上实现了异步非阻塞,之前我们学过的NIO只是非阻塞而非异步。而AIO它不需要通过多路复用器对注册的通道的进行轮训操作即可实现异步读写,从而简化了NIO编程模型。也可以称为NIO2.0,这种模式才是真正的属于异步非阻塞的模型。
AsynchronousServerScoketChannel
AsynchronousScoketChanel