day13-NIO、AIO
今日目标:
NIO
AIO
第一章 Java中各种IO的概述
1.阻塞与非阻塞
阻塞: 当调用某个方法时,方法任务没有结束,不会返回结果,程序无法继续执行
非阻塞: 当调用某个方法时,无论方法任务是否完成,直接返回,后续可以通过其他条件判断任务是否完成
2.同步与异步
同步: 当调用某个方法时,可以等待该方法结束后再返回,也可以直接返回,后续通过其他条件判断任务是否完成
异步: 当调用某个方法时,无论方法任务是否完成,直接返回,后续不需要自己判断,而是任务完成之后会通知我们
3.BIO,NIO,AIO的介绍
BIO:同步阻塞的IO(传统的IO流)
NIO:同步非阻塞的IO(也可以是同步阻塞的)
NIO中主要有三大类: 缓冲区,通道,选择器
AIO:异步的IO
回调函数
第二章 NIO-Buffer类(了解)
1.Buffer的介绍和种类
-
什么是Buffer
就是NIO中的缓冲区,作用是存储临时数据的地方,其本质就是一个数组
-
Buffer的一般操作步骤
1.写入数据到Buffer中 2.调用flip()方法(把Buffer切换成读模式) 3.从Buffer中读取数据 4.调用clear()方法或者compact()方法(清空缓冲区,并切换成写模式)
-
Buffer的种类
ByteBuffer[最常用的] CharBuffer DoubleBuffer FloatBuffer IntBuffer LongBuffer ShortBuffer
2.ByteBuffer的三种创建方式
public class BufferDemo01 {
public static void main(String[] args) {
//1.在堆区申请空间(间接缓冲区)
ByteBuffer b1 = ByteBuffer.allocate(10);
System.out.println(Arrays.toString(b1.array()));
//2.在系统内容中申请空间(直接缓冲区)
ByteBuffer b2 = ByteBuffer.allocateDirect(20);
System.out.println(b2);
//间接缓冲区创建和销毁效率高于直接缓冲区
//间接缓冲区的操作效率低于直接缓冲区
//3.间接缓冲区
byte[] bs = new byte[10];
ByteBuffer b3 = ByteBuffer.wrap(bs);
}
}
3.ByteBuffer的三种添加数据方式
public class BufferDemo02 {
public static void main(String[] args) {
//1.创建一个ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(10);
//2.添加数据
//a.一次加一个
buffer.put((byte)10);
buffer.put((byte)20);
buffer.put((byte)30);
System.out.println(Arrays.toString(buffer.array()));
//b.一次加一个数组
byte[] bs = {40,50,60};
buffer.put(bs);
System.out.println(Arrays.toString(buffer.array()));
//c.一次加一个数组的一部分
byte[] bs1 = {70,80,90};
buffer.put(bs1,1,2);
System.out.println(Arrays.toString(buffer.array()));
}
}
4.ByteBuffer的容量-capacity
Buffer的容量(capacity)是指:Buffer所能够包含的元素的最大数量
public class BufferDemo03 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
//2.获取容量
System.out.println("容量是:"+buffer.capacity()); -- 获取容量
//3.添加
buffer.put((byte)10);
buffer.put((byte)20);
//4.再次获取容量
System.out.println("容量是:"+buffer.capacity()); -- 获取容量
}
}
5.ByteBuffer的限制-limit
限制limit是指:第一个不应该读取或写入元素的index索引
public class BufferDemo04 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(15);
//2.查看限制
System.out.println("限制:"+buffer.limit());
//3.修改限制
buffer.limit(5);
System.out.println("限制:"+buffer.limit());
//4.添加数据
buffer.put((byte) 10);
buffer.put((byte) 10);
buffer.put((byte) 10);
buffer.put((byte) 10);
buffer.put((byte) 10);
System.out.println(Arrays.toString(buffer.array()));
//5.再次添加,报错,因为限制是5,也就说5索引开始不能添加数据了
buffer.put((byte) 10);
}
}
6.ByteBuffer的位置-position
位置position是指:当前要操作的元素索引(位置必须大于等于0,且小于等于限制)
public class BufferDemo05 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
//2.查看位置
System.out.println("当前位置:" + buffer.position());
//3.添加
buffer.put((byte) 10);
System.out.println(Arrays.toString(buffer.array()));
//3.再次查看位置
System.out.println("当前位置:" + buffer.position());
//4.再次添加
buffer.put((byte) 20);
System.out.println(Arrays.toString(buffer.array()));
//5.修改位置
buffer.position(5);
buffer.put((byte) 30);
System.out.println(Arrays.toString(buffer.array()));
//6.继续添加
buffer.put((byte)40);
buffer.put((byte)50);
buffer.put((byte)60);
buffer.put((byte)70);
System.out.println(Arrays.toString(buffer.array()));
System.out.println("当前位置:" + buffer.position());
//注意:位置可以等于限制,但是等于限制时,不能操作当前位置的元素
}
}
7.ByteBuffer的标记-mark
ByteBuffer的标记-mark: 做一个标记(mark = position),当调用reset方法时,(position = mark)
public class BufferDemo06 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
//2.添加元素
buffer.put((byte)10);
buffer.put((byte)20);
buffer.put((byte)30);
//3.当前位置是3,做一个标记
buffer.mark();
//4.添加元素
buffer.put((byte)40);
buffer.put((byte)50);
buffer.put((byte)60);
System.out.println(Arrays.toString(buffer.array()));
//5.重置缓冲区
buffer.reset();
buffer.put((byte)70);
buffer.put((byte)80);
System.out.println(Arrays.toString(buffer.array()));
}
}
8. ByteBuffer的其他方法
public void clear(); 还原缓冲区的状态
a.将position设置为:0
b.将限制limit设置为容量
c.丢弃标记mark
public class BufferDemo07 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前没有标记");
//2.添加数据
buffer.put((byte)10);
buffer.put((byte)20);
buffer.put((byte)30);
buffer.put((byte)40);
buffer.limit(6);
buffer.mark();
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前有标记");
//3.clear
System.out.println("====clear方法===");
buffer.clear();
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前没有标记");
}
}
public Buffer flip():缩小limit的范围(理解成切换模式,把写模式切换读模式)
a.将limit设置为当前position位置
b.将当前position位置设置为0
c.丢弃标记
public class BufferDemo08 {
public static void main(String[] args) {
//1.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前没有标记");
//2.添加数据
buffer.put((byte)10);
buffer.put((byte)20);
buffer.put((byte)30);
buffer.put((byte)40);
buffer.mark();
//3.打印
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前有标记");
//4.flip
System.out.println("=====flip()====");
buffer.flip();
System.out.println("当前位置:"+buffer.position());
System.out.println("当前限制:"+buffer.limit());
System.out.println("当前没有标记");
}
}
public Buffer rewind():重绕此缓冲区。
a.把position置为0
b.limit不变
c.丢弃标记
第三章 Channel(通道)(了解)
1. Channel介绍和分类
Channel称为通道,用于读或者写数据,和IO流相似,但是不同的是IO流是单向的,而通道是双向的
Channel的分类
FileChannel:文件通道
DatagramChannel:基于UDP协议数据传输的通道
SocketChannel:基于TCP协议中客户端的通道
ServerSocketChannel:基于TCP协议中服务器端的通道
2. FileChannel类的基本使用
public class FileChannelDemo {
public static void main(String[] args) throws IOException {
//1.创建2个流
FileInputStream fis = new FileInputStream("1.png");
FileOutputStream fos = new FileOutputStream("copy.png");
//2.获取通道
FileChannel channelIn = fis.getChannel();
FileChannel channelOut = fos.getChannel();
//3.复制文件
ByteBuffer buffer = ByteBuffer.allocate(1024); //position = 0,limit = 1024
int eof = 0;
while((eof =channelIn.read(buffer)) != -1){ // position = 1024,limit = 1024
//切换成读模式
buffer.flip(); // position = 0 limit = 1024
//开始写
channelOut.write(buffer);
//清空
buffer.clear(); //position = 0 limit = 1024 丢弃标记
}
channelOut.close();
channelIn.close();
fos.close();
fis.close();
}
}
3. FileChannel结合MappedByteBuffer实现高效读写
public class FileChannelAndMappedByteBufferDemo {
public static void main(String[] args) throws IOException {
//1.创建两个文件的对象
RandomAccessFile source = new RandomAccessFile("1.png", "r"); // r表示只读模式。
RandomAccessFile target = new RandomAccessFile("copy.png", "rw"); //rw表示可读可写
//2.获取通道
FileChannel inChannel = source.getChannel();
FileChannel outChannel = target.getChannel();
//3.映射缓冲区(镜像)
long size = inChannel.size();
MappedByteBuffer mbbi = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
MappedByteBuffer mbbo = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
//4.复制
System.out.println("开始...");
long start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
byte b = mbbi.get(i);//读取一个字节
mbbo.put(i, b);//将字节添加到mbbo中
}
long end = System.currentTimeMillis();
System.out.println("用时: " + (end - start) + " 毫秒");
//5.释放资源
outChannel.close();
inChannel.close();
target.close();
source.close();
}
}
注意: 使用MappedByteBuffer进行文件复制时,要求文件的大小不能操作2GB
如果文件的大小在2GB以上,使用分块进行映射复制
4. SocketChannel和ServerSocketChannel的实现连接
-
C/S阻塞模式
==================NIO使用同步阻塞的=================== SocketChannel 其实就是我们以前用的Socket public class SocketChannelDemo01 { public static void main(String[] args) throws IOException { //1.开启一个客户端通道 SocketChannel channel = SocketChannel.open(); //2.连接服务器 channel.connect(new InetSocketAddress("127.0.0.1", 8888)); } } ServerSocketChannel 其实就是我们以前用的ServerSocket public class ServerSocketChannelDemo01 { public static void main(String[] args) throws IOException { //1.开启服务器通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.绑定本地的端口号 serverSocketChannel.bind(new InetSocketAddress(8888)); //3.接收客户端通道 SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("有客户端连接了..."); } }
-
C/S非阻塞模式
==================NIO使用同步非阻塞的=================== //非阻塞的客户端 public class SocketChannelDemo02 { public static void main(String[] args) throws IOException { //1.开启一个客户端通道 SocketChannel channel = SocketChannel.open(); //2.连接服务器 channel.configureBlocking(false); // 开启NIO的非阻塞功能 channel.connect(new InetSocketAddress("127.0.0.1", 8888)); System.out.println("程序继续执行..."); } } //非阻塞的服务器 public class ServerSocketChannelDemo02 { public static void main(String[] args) throws IOException { //1.开启服务器通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.绑定本地的端口号 serverSocketChannel.bind(new InetSocketAddress(8888)); //3.接收客户端通道 serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能 SocketChannel socketChannel = serverSocketChannel.accept(); if (socketChannel == null) { System.out.println("无客户端"); }else{ System.out.println("有客户端来了.."); } } }
-
使用C阻塞模式和S非阻塞模式,实现连接
public class SocketChannelDemo03 { public static void main(String[] args){ while (true) { try { //1.开启一个客户端通道 SocketChannel channel = SocketChannel.open(); //2.连接服务器 channel.connect(new InetSocketAddress("127.0.0.1", 8888)); System.out.println("连接成功..."); break; } catch (IOException ie) { System.out.println("连接失败..3秒后重试.."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public class ServerSocketChannelDemo03 { public static void main(String[] args) throws IOException { //1.开启服务器通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.绑定本地的端口号 serverSocketChannel.bind(new InetSocketAddress(8888)); //3.接收客户端通道 serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能 while (true) { SocketChannel socketChannel = serverSocketChannel.accept(); if (socketChannel != null) { System.out.println("有客户端来了.."); break; } else { System.out.println("暂时无客户端,等待3秒..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
5. SocketChannel和ServerSocketChannel的实现通信
public class SocketChannelDemo04 {
public static void main(String[] args){
while (true) {
try {
//1.开启一个客户端通道
SocketChannel channel = SocketChannel.open();
//2.连接服务器
channel.connect(new InetSocketAddress("127.0.0.1", 8888));
System.out.println("连接成功...");
//3.给服务器发送数据
byte[] bs = "你好,我是NIO客户端".getBytes();
ByteBuffer buffer = ByteBuffer.wrap(bs);
channel.write(buffer);
//4.接收服务器的回复
ByteBuffer buffer1 = ByteBuffer.allocate(100);
channel.read(buffer1);
buffer1.flip();
System.out.println("客户端说:"+new String(buffer1.array(),0,buffer1.limit()));
//5.释放资源
channel.close();
break;
} catch (IOException ie) {
System.out.println("连接失败..3秒后重试..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class ServerSocketChannelDemo04 {
public static void main(String[] args) throws IOException {
//1.开启服务器通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.绑定本地的端口号
serverSocketChannel.bind(new InetSocketAddress(8888));
//3.接收客户端通道
serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
System.out.println("有客户端来了..");
//4.接收客户端发送的数据
ByteBuffer buffer = ByteBuffer.allocate(100);
socketChannel.read(buffer);
buffer.flip();
System.out.println("客户端说:"+new String(buffer.array(),0,buffer.limit()));
//5.回数据
byte[] bs = "你也好,我是NIO的服务器".getBytes();
ByteBuffer buffer1 = ByteBuffer.wrap(bs);
socketChannel.write(buffer1);
//6.释放资源
socketChannel.close();
serverSocketChannel.close();
break;
} else {
System.out.println("暂时无客户端,等待3秒...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
第四章 Select(选择器)(了解)
4.1 多路复用器(Selector)的概念
如果不使用多路复用器,那么每个服务器通道都需要开启一个线程
如果使用多路复用器,那么多个服务器通道我们只需要注册到多路复用器中,开启一个线程即可
4.2 选择器Selector_服务器端实现多路注册
创建一个选择器:
Selector selector = Selector.open();
注册Channel到Selector中:
channel.configureBlocking(false); -- 设置通道为非阻塞
SelectionKey key = channel.register(selector,SelectionKey.OP_ACCEPT);
public class SelectorDemo01 {
public static void main(String[] args) throws IOException {
//1.创建三个服务器通道
ServerSocketChannel serverSocketChannel1 = ServerSocketChannel.open();
serverSocketChannel1.configureBlocking(false);
serverSocketChannel1.bind(new InetSocketAddress(7777));
ServerSocketChannel serverSocketChannel2 = ServerSocketChannel.open();
serverSocketChannel2.configureBlocking(false);
serverSocketChannel2.bind(new InetSocketAddress(8888));
ServerSocketChannel serverSocketChannel3 = ServerSocketChannel.open();
serverSocketChannel3.configureBlocking(false);
serverSocketChannel3.bind(new InetSocketAddress(9999));
//2.创建一个多路复用器
Selector selector = Selector.open();
//3.将三个通道注册到选择器中
serverSocketChannel1.register(selector, SelectionKey.OP_ACCEPT);
serverSocketChannel2.register(selector, SelectionKey.OP_ACCEPT);
serverSocketChannel3.register(selector, SelectionKey.OP_ACCEPT);
}
}
4.3 Selector中的常用方法
public Set<SelectionKey> keys(); 返回集合,表示已经注册到本选择器所有服务器信息封装后对象
public Set<SelectionKey> selectedKeys();返回集合,表示本选择器中已经被客户端连接所有服务器信息封装后对象
public int select(); 此方法返回一共有多少个服务器通道被连接,如果没有,则阻塞
4.4.Selector实现多路连接
public class SelectorDemo02 {
public static void main(String[] args) {
new Thread(() -> {
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("7777客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 7777));
System.out.println("7777客户端连接成功....");
break;
} catch (IOException e) {
System.out.println("7777异常重连");
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
new Thread(() -> {
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("8888客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 8888));
System.out.println("8888客户端连接成功....");
break;
} catch (IOException e) {
System.out.println("8888异常重连");
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
}
}
public class SelectorDemo03 {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel channelA = ServerSocketChannel.open();
channelA.configureBlocking(false);
channelA.bind(new InetSocketAddress(7777));
ServerSocketChannel channelB = ServerSocketChannel.open();
channelB.configureBlocking(false);
channelB.bind(new InetSocketAddress(8888));
ServerSocketChannel channelC = ServerSocketChannel.open();
channelC.configureBlocking(false);
channelC.bind(new InetSocketAddress(9999));
//获取选择器
Selector selector = Selector.open();
//注册三个通道
channelA.register(selector, SelectionKey.OP_ACCEPT);
channelB.register(selector, SelectionKey.OP_ACCEPT);
channelC.register(selector, SelectionKey.OP_ACCEPT);
//打印一些数据
//获取已注册通道的集合
Set<SelectionKey> keys = selector.keys();
System.out.println("注册通道数量:" + keys.size());
System.out.println("-------------");
//获取已连接通 道的集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("已连接的通道数量:" + selectionKeys.size());
System.out.println("-------------");
System.out.println("【服务器】等待连接......");
//此方法会"阻塞"
int selectedCount = selector.select();
System.out.println("连接数量:" + selectedCount);
System.out.println("-------------");
Set<SelectionKey> keys1 = selector.keys();
System.out.println("注册通道数量:" + keys1.size());
Set<SelectionKey> selectionKeys1 = selector.selectedKeys();
System.out.println("已连接的通道数量:" + selectionKeys1.size());
}
}
4.4 Selector多路信息接收测试
public class SelectorDemo04 {
public static void main(String[] args) {
//两个线程,模拟两个客户端,分别连接服务器的7777,8888端口
new Thread(() -> {
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("7777客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 7777));
System.out.println("7777客户端连接成功....");
//发送信息
ByteBuffer outBuf = ByteBuffer.allocate(100);
outBuf.put("我是客户端,连接7777端口".getBytes());
outBuf.flip();
socket.write(outBuf);
break;
} catch (IOException e) {
System.out.println("7777异常重连");
}
}
}).start();
new Thread(() -> {
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("8888客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 8888));
System.out.println("8888客户端连接成功....");
//发送信息
ByteBuffer outBuf = ByteBuffer.allocate(100);
outBuf.put("我是客户端,连接8888端口".getBytes());
outBuf.flip();
socket.write(outBuf);
break;
} catch (IOException e) {
System.out.println("8888异常重连");
}
}
}).start();
new Thread(() -> {
while (true) {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("9999客户端连接服务器......");
socket.connect(new InetSocketAddress("localhost", 9999));
System.out.println("9999客户端连接成功....");
//发送信息
ByteBuffer outBuf = ByteBuffer.allocate(100);
outBuf.put("我是客户端,连接9999端口".getBytes());
outBuf.flip();
socket.write(outBuf);
break;
} catch (IOException e) {
System.out.println("9999异常重连");
}
}
}).start();
}
}
public class SelectorDemo05 {
public static void main(String[] args) throws IOException {
//1.同时监听三个端口:7777,8888,9999
ServerSocketChannel serverChannel1 = ServerSocketChannel.open();
serverChannel1.bind(new InetSocketAddress(7777));
serverChannel1.configureBlocking(false);
ServerSocketChannel serverChannel2 = ServerSocketChannel.open();
serverChannel2.bind(new InetSocketAddress(8888));
serverChannel2.configureBlocking(false);
ServerSocketChannel serverChannel3 = ServerSocketChannel.open();
serverChannel3.bind(new InetSocketAddress(9999));
serverChannel3.configureBlocking(false);
//2.获取一个选择器
Selector selector = Selector.open();
//3.注册三个通道
SelectionKey key1 = serverChannel1.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey key2 = serverChannel2.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey key3 = serverChannel3.register(selector, SelectionKey.OP_ACCEPT);
//4.循环
while (true) {
System.out.println("等待客户端连接...");
int keyCount = selector.select();
System.out.println("连接数量:" + keyCount);
//遍历已连接的每个通道的SelectionKey
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey nextKey = it.next();
System.out.println("获取通道...");
ServerSocketChannel channel = (ServerSocketChannel) nextKey.channel();
System.out.println("等待【" + channel.getLocalAddress() + "】 通道数据...");
SocketChannel socketChannel = channel.accept();
//接收数据
ByteBuffer inBuf = ByteBuffer.allocate(100);
socketChannel.read(inBuf);
inBuf.flip();
String msg = new String(inBuf.array(), 0, inBuf.limit());
System.out.println("【服务器】接收到通道【" + channel.getLocalAddress() + "】的信息:" + msg);
//移除该通道
it.remove();
}
}
}
}
第五章 AIO(异步、非阻塞)(了解)
5.1 AIO概述
AIO:异步的IO
当调用一个方法时,不需要等待方法任务结束,直接返回,当方法任务结束后,会回调函数通知我们我们
5.2 AIO 异步非阻塞连接
public class AIODemo01 {
public static void main(String[] args) throws IOException {
//1.异步的客户端通道
AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
//2.连接服务器
asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
System.out.println("连接成功...");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("连接失败...");
}
});
System.out.println("程序继续...");
}
}
public class AIODemo02 {
public static void main(String[] args) throws IOException {
//1.创建异步服务器通道
AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
//2.绑定端口
asynchronousServerSocketChannel.bind(new InetSocketAddress(8888));
//3.接收客户端
asynchronousServerSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
System.out.println("连接成功...");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("连接失败...");
}
});
System.out.println("服务器继续....");
}
}
5.3 AIO同步非阻塞读写数据
public class AIODemo03 {
public static void main(String[] args) throws InterruptedException, IOException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("客户端连接成功");
Future<Integer> writeFuture = socketChannel.write(ByteBuffer.wrap("我来自客户 端...".getBytes()));
//同步写
try {
System.out.println("写入大小:" + writeFuture.get());
socketChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("客户端失败!");
}
});
System.out.println("客户端继续");
Thread.sleep(30000);
}
}
public class AIODemo04 {
public static void main(String[] args) throws InterruptedException, IOException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
//异步的accept()
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
//有客户端连接成功的回调函数
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
System.out.println("服务器端接收到连接...");
ByteBuffer byteBuffer = ByteBuffer.allocate(30);
Future<Integer> readFuture = result.read(byteBuffer);
//同步读
try {
System.out.println("读取信息:" + new String(byteBuffer.array(), 0, readFuture.get()));
result.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//IO操作失败时的回调函数
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("IO操作失败!");
}
});
System.out.println("服务器端继续....");
Thread.sleep(30000);
}
}
5.4 AIO异步非阻塞读写
public class AIODemo05 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8888), null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
socketChannel.write(ByteBuffer.wrap("你好服务器".getBytes()), null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
System.out.println("输出完毕!");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("输出失败!");
}
});
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("【客户端】异常!");
}
});
System.out.println("客户端继续...");
Thread.sleep(30000);
}
}
public class AIODemo06 {
public static void main(String[] args) throws InterruptedException, IOException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel ch, Void attachment) {
// serverSocketChannel.accept(null, this);
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.MAX_VALUE / 300);
System.out.println("【服务器】read开始...");
ch.read(byteBuffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (result == -1) {
System.out.println("客户端没有传输数据就close了...");
}
System.out.println("服务器读取数据:" + new String(byteBuffer.array(), 0, result));
try {
ch.close();
System.out.println("服务器关闭!");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
System.out.println(attachment);
System.out.println("【服务器】异常");
}
});
System.out.println("【服务器】read结束...");
}
@Override
public void failed(Throwable exc, Void attachment) {
}
});
System.out.println("服务器开始循环...");
Thread.sleep(30000);
}
}
总结:
"能够说出同步和异步的概念
同步: 可能是阻塞的,也可以是非阻塞
异步: 一定是非阻塞的,并且调用方法时,不等待方法任务结束,直接返回,后期通过回调函数通知程序员
"能够说出阻塞和非阻塞的概念
阻塞: 调用方法时,等待方法任务结束才返回
非阻塞: 调用方法时,不等待方法任务结束,直接返回,后期通过其他方式判断任务是否结束
能够创建和使用ByteBuffer
能够使用MappedByteBuffer实现高效读写
能够使用ServerSocketChannel和SocketChannel实现连接并收发信息
能够说出Selector选择器的作用
能够使用Selector选择器
能够说出AIO的特点