Java=NIO,AIO详解
1.NIO的Buffer,Channel,Selector(选择器,多路复用器)
2.AIO是异步非阻塞IO
多路复用的概念: 选择器Selector 是NIO中的重要技术之一。他与SelectableChannel 联合使用实现了非阻塞的多路复用,使他可以节省CPU的资源,提高程序的运行效率
多路 是指,服务器端同时监听多个 端口 的情况,每个端口都要监听多个客户端的连接
服务器端的非多路复用的效果:
如果不使用 多路复用,服务器端需要开很多线程处理每个端口的请求,如果在高并发环境下,造成系统性能下降。
服务器端的多路复用效果:
使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少cpu切换时间,在高并发,高频段业务环境下有非常重要的优势。
选择器Selector
Selector称为 选择器,也称为 多路复用器,他可以注册到很多个 Channel上,监听各个Channel上发生的事件,并且能够根据事件情况决定Channel读写,这样,通过一个线程管理多个Channel,就可以处理大量网络连接了
有了Selector,我们可以利用一个线程来处理所有的Channel,线程之前的切换对操作系统来说代价太高,并且每一个线程也会占用一定的系统资源,所以,对系统来说使用的线程越少越好。
创建一个Selector:
创建Selector的方式
Selector selector = Selector.open();
注册Channel到Selector
为了能让Channel 和 Selector 配合使用,我们需要把Channel注册到 Selector上,通过调用channel.register()方法来实现注册
channel . configureBlocking ( false );
SelectionKey key = channel . register ( selector , SelectionKey . OP_READ );
注意
注册的 Channel 必须设置成异步模式才可以 , 否则异步 IO 就无法工作,这就意味着我们不能把一
个 FileChannel 注册到 Selector ,因为 FileChannel 没有异步模式,但是网络编程中的 SocketChannel 是
可以的。
register() 方法的第二个参数: 是一个 int 值,意思是在 通过 Selector 监听 Channel 时对什么事件感兴
趣 。可以监听四种不同类型的事件,而且可以使用 SelectionKey 的四个常量表示:
1. 连接就绪 -- 常量: SelectionKey.OP_CONNECT
2. 接收就绪 -- 常量: SelectionKey.OP_ACCEPT (ServerSocketChannel 在注册时只能使用此项 )
3. 读就绪 -- 常量: SelectionKey.OP_READ
4. 写就绪 -- 常量: SelectionKey.OP_WRITE
注意:对于 ServerSocketChannel 在注册时,只能使用 OP_ACCEPT ,否则抛出异常。
public class SelectorDemo01 {
public static void main(String[] args) throws IOException {
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.bind(new InetSocketAddress(7777));
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.bind(new InetSocketAddress(8888));
ServerSocketChannel server3 = ServerSocketChannel.open();
server3.configureBlocking(false);
server3.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
se,那么rver1.register(selector, SelectionKey.OP_ACCEPT);
server2.register(selector, SelectionKey.OP_ACCEPT);
server3.register(selector, SelectionKey.OP_ACCEPT);
}
}
Selector中的常用方法
Selector 的 keys() 方法
此方法返回一个 Set 集合,表示:已注册通道的集合。每个已注册通道封装为一个
SelectionKey 对象。
Selector 的 selectedKeys() 方法
此方法返回一个 Set 集合,表示:当前已连接的通道的集合。每个已连接通道统一封装为一个
SelectionKey 对象。
Selector 的 select() 方法
此方法会阻塞,直到有至少 1 个客户端连接。
此方法会返回一个 int 值,表示有几个客户端连接了服务器。
示例:
public class SelectorDemo02 {
public static void main(String[] args) throws IOException {
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.bind(new InetSocketAddress(7777));
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.bind(new InetSocketAddress(8888));
ServerSocketChannel server3 = ServerSocketChannel.open();
server3.configureBlocking(false);
server3.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
server1.register(selector, SelectionKey.OP_ACCEPT);
server2.register(selector, SelectionKey.OP_ACCEPT);
server3.register(selector, SelectionKey.OP_ACCEPT);
Set<SelectionKey> keys = selector.keys();
System.out.println("注册通道数量:" + keys.size());
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());
}
}
public class SocketChannelDemo {
public static void main(String[] args) {
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("7777客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 7777));
System.out.println("7777客户端连接成功....");
} catch (IOException e) {
System.out.println("7777异常重连");
}
}).start();
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("8888客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 8888));
System.out.println("8888客户端连接成功....");
} catch (IOException e) {
System.out.println("8888异常重连");
}
}).start();
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("9999客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
System.out.println("9999客户端连接成功....");
} catch (IOException e) {
System.out.println("9999异常重连");
}
}).start();
}
}
Selector实现多路连接:
public class SelectorDemo02 {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.bind(new InetSocketAddress(7777));
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.bind(new InetSocketAddress(8888));
ServerSocketChannel server3 = ServerSocketChannel.open();
server3.configureBlocking(false);
server3.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
server1.register(selector, SelectionKey.OP_ACCEPT);
server2.register(selector, SelectionKey.OP_ACCEPT);
server3.register(selector, SelectionKey.OP_ACCEPT);
Set<SelectionKey> keys = selector.keys();
System.out.println("注册通道数量:" + keys.size());
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("已连接通道数量:" + selectionKeys.size());
System.out.println("----------------------------------------------");
while (true) {
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());
System.out.println("============处理被连接的服务器通道============");
Iterator<SelectionKey> it = selectionKeys1.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
System.out.println("监听端口:" + channel.getLocalAddress());
SocketChannel accept = channel.accept();
System.out.println(accept);
System.out.println("写与该客户端进行交互的代码....");
it.remove();
}
System.out.println("休息5秒......");
Thread.sleep(5000);
System.out.println();
}
}
}
public class SocketChannelDemo {
public static void main(String[] args) {
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("7777客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 7777));
System.out.println("7777客户端连接成功....");
} catch (IOException e) {
System.out.println("7777异常重连");
}
}).start();
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("8888客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 8888));
System.out.println("8888客户端连接成功....");
} catch (IOException e) {
System.out.println("8888异常重连");
}
}).start();
new Thread(() -> {
try (SocketChannel socket = SocketChannel.open()) {
System.out.println("9999客户端连接服务器......");
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
System.out.println("9999客户端连接成功....");
} catch (IOException e) {
System.out.println("9999异常重连");
}
}).start();
}
}
Selector多路信息接收:
public class SelectorDemo02 {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.bind(new InetSocketAddress(7777));
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.bind(new InetSocketAddress(8888));
ServerSocketChannel server3 = ServerSocketChannel.open();
server3.configureBlocking(false);
server3.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
server1.register(selector, SelectionKey.OP_ACCEPT);
server2.register(selector, SelectionKey.OP_ACCEPT);
server3.register(selector, SelectionKey.OP_ACCEPT);
Set<SelectionKey> keys = selector.keys();
System.out.println("注册通道数量:" + keys.size());
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("已连接通道数量:" + selectionKeys.size());
System.out.println("----------------------------------------------");
while (true) {
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());
System.out.println("============处理被连接的服务器通道============");
Iterator<SelectionKey> it = selectionKeys1.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
System.out.println("监听端口:" + channel.getLocalAddress());
SocketChannel accept = channel.accept();
System.out.println("写与该客户端进行交互的代码....");
ByteBuffer inBuf = ByteBuffer.allocate(1000);
accept.read(inBuf);
inBuf.flip();
String msg = new String(inBuf.array(), 0, inBuf.limit());
System.out.println("【服务器】接收到通道【" + channel.getLocalAddress() + "】的信息:" + msg);
it.remove();
}
System.out.println("休息5秒......");
Thread.sleep(5000);
System.out.println();
}
}
}
关于 SelectionKey
- 当一个 " 通道 " 注册到选择器 Selector 后,选择器 Selector 内部就创建一个 SelectionKey 对象,里面封
装了这个通道和这个选择器的映射关系。
- 通过 SelectionKey 的 channel() 方法,可以获取它内部的通道对象。
解决 select() 不阻塞,导致服务器端死循环的问题
原因:在将 " 通道 " 注册到 " 选择器 Selector" 时,我们指定了关注的事件 SelectionKey.OP_ACCEPT ,
而我们获取到管道对象后,并没有处理这个事件,所以导致 select() 方法一直循环。
解决:处理 SelectionKey.OP_ACCEPT 事件
二。AIO (异步,非阻塞)AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。
对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,
IO 操作本身是同步的。
但是对 AIO 来说,则更加进了一步,它不是在 IO 准备好时再通知线程,而是在 IO 操作已经完成后,再给
线程发出通知。因此 AIO 是不会阻塞的,此时我们的业务逻辑将变成一个回调函数,等待 IO 操作完成
后,由系统自动触发。
与 NIO 不同,当进行读写操作时,只须直接调用 API 的 read 或 write 方法即可。这两种方法均为异步的,
对于读操作而言,当有流可读取时,操作系统会将可读的流传入 read 方法的缓冲区,并通知应用程序;
对于写操作而言,当操作系统将 write 方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以
理解为, read/write 方法都是异步的,完成后会主动调用回调函数。
在 JDK1.7 中,这部分内容被称作
NIO.2,主要在Java.nio.channels包下增加了下面四个异步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
在AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,这个类提供了一个open()
静态工厂,一个bind()方法用于绑定服务端IP地址(还有端口号),另外还提供了accept()用于接收用户
连接请求。在客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法
外,还提供了read和write方法。
在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO中的
事件处理类是CompletionHandler<V,A>,这个接口定义了如下两个方法,分别在异步操作成功和失败
时被回调。
void completed(V result, A attachment);
void failed(Throwable exc, A attachment);
ASynchronized 异步非阻塞的IO
AIO的分类:
异步的文件通道 AsynchronousFileChannel
异步的客户端通道: AsynchronousSocketChannel
异步的服务器通道: AsynchronousServerSocketChannel
异步的UDP协议通道: AsynchronousDatagramChannel
AIO的异步:
表现两个方面:
a.连接时,可以使用异步(调用连接的方法时,非阻塞,连接成功之后会以方法回调的机制通知我们)
b.读写数据时,可以使用异步(调用read方法时,非阻塞,等数据接收到之后以方法调用的机制通知我们)
AIO异步IO非阻塞连接建立
异步非阻塞客户端通道
public class AIOSocketChannelDemo01 {
public static void main(String[] args) throws IOException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("连接服务器成功...");
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接服务器失败...");
}
});
System.out.println("程序继续执行....");
while (true) {
}
}
}
异步非阻塞服务器通道
public class AIOServerSocketChannelDemo01 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("有客户端连接....");
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("客户端连接失败...");
}
});
System.out.println("程序继续执行..");
while (true) {
}
}
}
2.AIO同步读写数据:
public class AIOSocketChannelDemo01 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("连接服务器成功...");
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("你好我是AIO客户端...".getBytes());
buffer.flip();
socketChannel.write(buffer);
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接服务器失败...");
}
});
System.out.println("程序继续执行....");
}
}
public class AIOServerSocketChannelDemo01 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
System.out.println("有客户端连接....");
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> future = socketChannel.read(buffer);
try {
byte[] array = buffer.array();
System.out.println(Arrays.toString(array));
Integer len = future.get();
System.out.println(len);
buffer.flip();
System.out.println(new String(array,0,len));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("客户端连接失败...");
}
});
System.out.println("程序继续执行..");
}
}
3.AIO异步读写数据
public class AIOSocketChannelDemo01 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("连接服务器成功...");
ByteBuffer buffer = ByteBuffer.allocate(1000);
buffer.put("你好我是AIO客户端..".getBytes());
buffer.flip();
socketChannel.write(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("数据成功发送...");
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("数据发送失败...");
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接服务器失败...");
}
});
System.out.println("程序继续执行....");
Thread.sleep(5000);
}
}
public class AIOServerSocketChannelDemo01 {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
System.out.println("有客户端连接....");
ByteBuffer buffer = ByteBuffer.allocate(1000);
socketChannel.read(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("数据读取完毕..");
System.out.println("接收到数据的长度:"+result);
System.out.println("接收到的数据是:" + new String(buffer.array(), 0, result));
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取数据失败...");
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("客户端连接失败...");
}
});
System.out.println("程序继续执行..");
while (true) {
Thread.sleep(1000);
}
}
}
other===================================================
one---
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class oneClient {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try (SocketChannel socketChannel = SocketChannel.open()){
System.out.println("7777 客户端连接服务器。。。。。。");
socketChannel.connect(new InetSocketAddress("127.0.0.1",7777));
System.out.println("7777 客户端连接成功。。。。。。");
ByteBuffer allocate = ByteBuffer.allocate(1024);
allocate.put("我是 7777 客户端".getBytes());
allocate.flip();
socketChannel.write(allocate);
}catch (IOException e){
e.printStackTrace();
System.out.println("7777连接异常");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try (SocketChannel socketChannel = SocketChannel.open()){
System.out.println("8888 客户端连接服务器。。。。。。");
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
System.out.println("8888 客户端连接成功。。。。。。");
ByteBuffer allocate = ByteBuffer.allocate(1024);
allocate.put("我是 8888 客户端".getBytes());
allocate.flip();
socketChannel.write(allocate);
}catch (IOException e){
e.printStackTrace();
System.out.println("8888 连接异常");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try (SocketChannel socketChannel = SocketChannel.open()){
System.out.println("9999 客户端连接服务器。。。。。。");
socketChannel.connect(new InetSocketAddress("127.0.0.1",9999));
System.out.println("9999 客户端连接成功。。。。。。");
ByteBuffer allocate = ByteBuffer.allocate(1024);
allocate.put("我是 9999 客户端".getBytes());
allocate.flip();
socketChannel.write(allocate);
}catch (IOException e){
e.printStackTrace();
System.out.println("9999 连接异常");
}
}
}).start();
}
}
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class oneService {
public static void main(String[] args) throws Exception{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(7777));
ServerSocketChannel serverSocketChannel01 = ServerSocketChannel.open();
serverSocketChannel01.configureBlocking(false);
serverSocketChannel01.bind(new InetSocketAddress(8888));
ServerSocketChannel serverSocketChannel02 = ServerSocketChannel.open();
serverSocketChannel02.configureBlocking(false);
serverSocketChannel02.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
serverSocketChannel01.register(selector,SelectionKey.OP_ACCEPT);
serverSocketChannel02.register(selector,SelectionKey.OP_ACCEPT);
Set<SelectionKey> keys = selector.keys();
System.out.println("=注册的通道数量="+keys.size());
Set<SelectionKey> selectionKeys = selector.selectedKeys();
System.out.println("=已连接的通道数量="+selectionKeys.size());
System.out.println("========================================================");
while (true){
System.out.println("【服务器】等待连接。。。");
int select = selector.select();
System.out.println("服务器连接的数量:"+select);
Set<SelectionKey> selectionKeys1 = selector.selectedKeys();
System.out.println("集合大小:"+selectionKeys1.size());
Iterator<SelectionKey> iterator = selectionKeys1.iterator();
while (iterator.hasNext()){
SelectionKey next = iterator.next();
ServerSocketChannel channel = (ServerSocketChannel) next.channel();
System.out.println("监听端口:"+channel.getLocalAddress());
SocketChannel accept = channel.accept();
ByteBuffer allocate = ByteBuffer.allocate(1024);
accept.read(allocate);
allocate.flip();
String string = new String(allocate.array(), 0, allocate.limit());
System.out.println("【服务器】接收到通道【"+channel.getLocalAddress()+"】的信息:"+string);
iterator.remove();
}
System.out.println("休息1秒");
Thread.sleep(1000);
}
}
}
two:AIO同步读写
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AIOClient {
public static void main(String[] args) throws Exception{
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("客户端连接成功");
socketChannel.write(ByteBuffer.wrap("我来自客户端8888端口的数据".getBytes()));
try {
socketChannel.close();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("客户端连接失败");
}
});
System.out.println("客户端继续");
}
}
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;
public class AIOService {
public static void main(String[] args) throws Exception{
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("服务器接收连接、、、");
ByteBuffer allocate = ByteBuffer.allocate(1024);
Future<Integer> read = result.read(allocate);
try {
Integer len = read.get();
allocate.flip();
String string = new String(allocate.array(), 0, len);
System.out.println("【服务器】收到客户端的数据为:"+string);
result.close();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("IO操作失败、、、");
}
});
System.out.println("服务器端继续,,,,");
while (true){
}
}
}
AIO异步读写
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AIOyibuClient {
public static void main(String[] args) throws Exception{
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999), null, new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
ByteBuffer byteBuffer = ByteBuffer.wrap("我是来自客户端的 9999 用户".getBytes());
socketChannel.write(byteBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("客户端数据--发送完毕");
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("客户端数据--发送失败");
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
while (true){
}
}
}
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AIOyibuSevice {
public static void main(String[] args) throws Exception{
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
System.out.println("服务器连接成功。。。");
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socketChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
if (result!=-1){
String string = new String(byteBuffer.array(), 0, result);
System.out.println("-服务器端收到客户端的数据为:"+string);
}
try{
socketChannel.close();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
while (true){
}
}
}
BIO、NIO和AIO的区别、三种IO的原理
IO
什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。
在 Java 编程中,直到最近一直使用 流 的方式完成 I/O。所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
BIO
Java BIO即Block I/O , 同步并阻塞的IO。
BIO就是传统的java.io包下面的代码实现。
NIO
什么是NIO? NIO 与原来的 I/O 有同样的作用和目的, 他们之间最重要的区别是数据打包和传输的方式。原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
AIO
Java AIO即Async非阻塞,是异步非阻塞的IO。
区别及联系
BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
NIO (New I/O):同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
AIO ( Asynchronous I/O):异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
各自适用场景
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
网址:Java=NIO,AIO详解 https://www.yuejiaxmz.com/news/view/83819
相关内容
10年Java面试总结:Java程序员面试必备的面试技巧Java 在生活中的 10 大应用
java连接mysql错误及解决方案整合
Java
JAVA题目
【Java数据结构】字符串常量池
Java程序员不得不会的124道面试题(含答案)
JAVA家政服务管理系统毕业设计 开题报告
037基于java+springboot的课外学习生活活动平台系统
java计算机毕业设计宠物管理系统(开题+程序+论文)