Java Nio
三个主要接口或类
buffer
chanel
selector
一:关于buffer
ByteBuffer.allocate(size)方法确定一个buff的大小 CharBuffer
buffer.put()
向buff中写入数据 写入类型决定于是什么类型的buff
一般步骤:
1,将数据写入到 Buffer 中.
2,调用 Buffer.flip()方法, 将 NIO Buffer 转换为读模式.
3,从 Buffer 中读取数据
4,调用 Buffer.clear() 或 Buffer.compact()方法, 将 Buffer 转换为写模式.
rewind() 主要针对于读模式. 在读模式时, 读取到 limit 后, 可以调用 rewind() 方法, 将读 position 置为0.
Buffer.rewind()方法可以重置 position 的值为0, 因此我们可以重新读取/写入 Buffer 了.
多次读取某个数据 可配合使用mark()方法和reset()方法
buffer 有三个属性 position limit和capacity分别表示当前读或写的指针所在位置
二:关于chanel
chanel只能与buffer交互
chanel.read(buff) 从buff中读数据到管道中
将buff中数据循环写入管道中
while(buffer.hasRemaining()){
chanel.write(buff)//向管道中写入buff中的数据
}
chanel 管道接受数据流 从网络tcp:SocketChannel,UDP:datagramChannel,文件:FileChannel等.
SocketChannel 是一个客户端用来进行 TCP 连接的 Channel.
SocketChannel socketChannel = SocketChannel.open();// 产生一个SocketChannel
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
ServerSocketChannel tcp服务端 监听某端口的socketChanel的链接
socketChannel.configureBlocking(false);// 设置channel为异步
Selector selector = Selector.open();// 创建选择器
channel.register(selector,SelectorKey.OP_READ)
注册Channel,第一个参数选择器,第二个参数指定对什么类型时间感兴趣,包含Connec,
Accept,Read,Write.
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; // 或运算,关注两种事情
三:关于Selector 选择器,选择哪个chanel处理数据
Selector selector = Selector.open();// 创建选择器
chanel注册到selector上
可以通过 Selector.select()方法获取对某件事件准备好了的 Channel, 即如果我们在注册 Channel 时, 对其的可写事件感兴趣, 那么当 select()返回时, 我们就可以获取 Channel 了.
注意, select()方法返回的值表示有多少个 Channel 可操作.如果 select()方法返回值表示有多个 Channel 准备好了, 那么我们可以通过 Selected key set 访问这个 Channel:
循环调用select()方法查看是否有channel就绪,
selector.selectedKeys() 返回可操作的事件集合
根据selectionKey 调用isAcceptable() 或isConnectable() isReadsble(),isWriteable(),等方法处理不同事件,然后移除处理完的事件keysIterator.remove();
注意, 在每次迭代时, 我们都调用 "keyIterator.remove()" 将这个 key 从迭代器中删除, 因为 select() 方法仅仅是简单地将就绪的 IO 操作放到 selectedKeys 集合中, 因此如果我们从 selectedKeys 获取到一个 key, 但是没有将它删除,
那么下一次 select 时, 这个 key 所对应的 IO 事件还在 selectedKeys 中.
Selector 的基本使用流程
1 通过 Selector.open() 打开一个 Selector.
2 将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set)
3 不断重复:
3.1 调用 select() 方法
3.2 调用 selector.selectedKeys() 获取 selected keys
3.3
迭代每个 selected key:
3.3.1 *从 selected key 中获取 对应的 Channel 和附加信息(如果有的话)
3.3.2 *判断是哪些 IO 事件已经就绪了, 然后处理它们. 如果是 OP_ACCEPT 事件, 则调用 "SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept()" 获取 SocketChannel, 并将它设置为 非阻塞的, 然后将这个 Channel 注册到 Selector 中.
3.3.4 *根据需要更改 selected key 的监听事件.
三个主要接口或类
buffer
chanel
selector
一:关于buffer
ByteBuffer.allocate(size)方法确定一个buff的大小 CharBuffer
buffer.put()
向buff中写入数据 写入类型决定于是什么类型的buff
一般步骤:
1,将数据写入到 Buffer 中.
2,调用 Buffer.flip()方法, 将 NIO Buffer 转换为读模式.
3,从 Buffer 中读取数据
4,调用 Buffer.clear() 或 Buffer.compact()方法, 将 Buffer 转换为写模式.
rewind() 主要针对于读模式. 在读模式时, 读取到 limit 后, 可以调用 rewind() 方法, 将读 position 置为0.
Buffer.rewind()方法可以重置 position 的值为0, 因此我们可以重新读取/写入 Buffer 了.
多次读取某个数据 可配合使用mark()方法和reset()方法
buffer 有三个属性 position limit和capacity分别表示当前读或写的指针所在位置
调用flip方法时,将limit属性设置为了当前的posion 限制只能读取limit个单位长度的数据
clear方法position属性设置为0,limit设置为capacity。rewind方法只是将position设置为0;
二:关于chanel
chanel只能与buffer交互
chanel.read(buff) 从buff中读数据到管道中
将buff中数据循环写入管道中
while(buffer.hasRemaining()){
chanel.write(buff)//向管道中写入buff中的数据
}
chanel 管道接受数据流 从网络tcp:SocketChannel,UDP:datagramChannel,文件:FileChannel等.
SocketChannel 是一个客户端用来进行 TCP 连接的 Channel.
SocketChannel socketChannel = SocketChannel.open();// 产生一个SocketChannel
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
ServerSocketChannel tcp服务端 监听某端口的socketChanel的链接
socketChannel.configureBlocking(false);// 设置channel为异步
Selector selector = Selector.open();// 创建选择器
channel.register(selector,SelectorKey.OP_READ)
注册Channel,第一个参数选择器,第二个参数指定对什么类型时间感兴趣,包含Connec,
Accept,Read,Write.
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; // 或运算,关注两种事情
三:关于Selector 选择器,选择哪个chanel处理数据
Selector selector = Selector.open();// 创建选择器
chanel注册到selector上
可以通过 Selector.select()方法获取对某件事件准备好了的 Channel, 即如果我们在注册 Channel 时, 对其的可写事件感兴趣, 那么当 select()返回时, 我们就可以获取 Channel 了.
注意, select()方法返回的值表示有多少个 Channel 可操作.如果 select()方法返回值表示有多个 Channel 准备好了, 那么我们可以通过 Selected key set 访问这个 Channel:
循环调用select()方法查看是否有channel就绪,
selector.selectedKeys() 返回可操作的事件集合
根据selectionKey 调用isAcceptable() 或isConnectable() isReadsble(),isWriteable(),等方法处理不同事件,然后移除处理完的事件keysIterator.remove();
注意, 在每次迭代时, 我们都调用 "keyIterator.remove()" 将这个 key 从迭代器中删除, 因为 select() 方法仅仅是简单地将就绪的 IO 操作放到 selectedKeys 集合中, 因此如果我们从 selectedKeys 获取到一个 key, 但是没有将它删除,
那么下一次 select 时, 这个 key 所对应的 IO 事件还在 selectedKeys 中.
Selector 的基本使用流程
1 通过 Selector.open() 打开一个 Selector.
2 将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set)
3 不断重复:
3.1 调用 select() 方法
3.2 调用 selector.selectedKeys() 获取 selected keys
3.3
迭代每个 selected key:
3.3.1 *从 selected key 中获取 对应的 Channel 和附加信息(如果有的话)
3.3.2 *判断是哪些 IO 事件已经就绪了, 然后处理它们. 如果是 OP_ACCEPT 事件, 则调用 "SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept()" 获取 SocketChannel, 并将它设置为 非阻塞的, 然后将这个 Channel 注册到 Selector 中.
3.3.4 *根据需要更改 selected key 的监听事件.
3.3.5 *将已经处理过的 key 从 selected keys 集合中删除.
完整范例:
public class NioEchoServer {
private static final int BUF_SIZE = 256;
private static final int TIMEOUT = 3000;
public static void main(String args[]) throws Exception {
// 打开服务端 Socket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 打开 Selector
Selector selector = Selector.open();
// 服务端 Socket 监听8080端口, 并配置为非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 将 channel 注册到 selector 中.
// 通常我们都是先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ
// 注册到 Selector 中.
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 通过调用 select 方法, 阻塞地等待 channel I/O 可操作
if (selector.select(TIMEOUT) == 0) {
System.out.print(".");
continue;
}
// 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪.
Iterator
keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 当获取一个 SelectionKey 后, 就要将它删除, 表示我们已经对这个 IO 事件进行了处理.
keyIterator.remove();
if (key.isAcceptable()) {
// 当 OP_ACCEPT 事件到来时, 我们就有从 ServerSocketChannel 中获取一个 SocketChannel,
// 代表客户端的连接
// 注意, 在 OP_ACCEPT 事件中, 从 key.channel() 返回的 Channel 是 ServerSocketChannel.
// 而在 OP_WRITE 和 OP_READ 中, 从 key.channel() 返回的是 SocketChannel.
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
//在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ 注册到 Selector 中.
// 注意, 这里我们如果没有设置 OP_READ 的话, 即 interest set 仍然是 OP_CONNECT 的话, 那么 select 方法会一直直接返回.
clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));
}
if (key.isReadable()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
long bytesRead = clientChannel.read(buf);
if (bytesRead == -1) {
clientChannel.close();
} else if (bytesRead > 0) {
key.interestOps(OP_READ | SelectionKey.OP_WRITE);
System.out.println("Get data length: " + bytesRead);
}
}
if (key.isValid() && key.isWritable()) {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.flip();
SocketChannel clientChannel = (SocketChannel) key.channel();
clientChannel.write(buf);
if (!buf.hasRemaining()) {
key.interestOps(OP_READ);
}
buf.compact();
}
}
}
}
}