1.Selector和Channel关系
Selector一般称为选择器,也叫多路复用器,NIO的核心组件,用于检查一个或多个Channel的状态是否处于可读、可写的状态。
2.可选择通道
(1)不是所有的channel都能被selector复用,就是要看channel是否继承SelectableChannel,如果继承了就是可以复用。例如:FileChannel是不能被selector复用的。
(2)一个通道可以被注册到多个选择骑上,但每个选择器只能被注册一次。在注册的时候,需要指定通道那些操作是选择器感兴趣的。
3.Channel注册到Selector
- Channel.register(Selector sel, int ops) 方法:将一个通道注册到一个选择器上。第一个参数,指定通道要注册的选择器。第二个参数指定选择器需要查询的通道操作。
- 可以供选择器查询的通道操作,包括如下四种(可以通过位或操作符表示多种操作类型感兴趣):
(1)可读SelectionKey.OP_READ
(2)可写SelectionKey.OP_WRITE
(3)连接SelectionKey.OP_CONNECT
(4)接收SelectionKey.OP_ACCEPT
3.1 选择键(SelectionKey)
(1)Channel注册后,一旦通道处于某种就绪的状态,就可以被选择器查询到。
(2)Selector不断查询Channel中操作的就绪状态,并挑选感兴趣的操作就绪状态,并放入选择键集合。
(3)一个选择键包含了特定通道域特定选择器之间的注册关系。
NIO就是根据对应的选择键,进行不同的业务逻辑处理。
3.2 Selector的使用方法
(1) Channel必须处于非阻塞模式下,否则会抛出IllegalBlockingModeException
(2)一个通道不一定支持所有的四种操作,比如ServerSocketChannel支持Accept操作,SocketChannel则不支持。可通过validOps()获取支持的操作集合。
- Selector的创建
// 创建Selector
Selector sl = Selector.open();
// 创建通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 非阻塞
serverSocketChannel.configureBlocking(false);
// 绑定连接
serverSocketChannel.bind(new InetSocketAddress(9999));
//将通道注册到选择器上
serverSocketChannel.register(sl, SelectionKey.OP_ACCEPT);
System.out.println(serverSocketChannel.validOps());
2.轮询查询就绪操作
//查询已经就绪通道操作
Set<SelectionKey> selectionKeys = sl.selectedKeys();
// 遍历集合
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
// 判断key的就绪状态操作
if (next.isAcceptable()) {
} else if (next.isConnectable()) {
} else if (next.isReadable()) {
} else {
}
iterator.remove();
}
4 实验案例
import com.sun.org.apache.xerces.internal.util.SynchronizedSymbolTable;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
/**
* @Des
* @Date
*/
public class Demo2 {
@Test
// 客户端代码
public void client() throws IOException {
// 获取通道,绑定主机和端口号
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
// 切换为非阻塞模式
socketChannel.configureBlocking(false);
// 创建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 写入buffer
byteBuffer.put(new Date().toString().getBytes());
// 读写模式转换
byteBuffer.flip();
// channel写入数据
socketChannel.write(byteBuffer);
// 清空buffer
byteBuffer.clear();
}
@Test
// 客户端代码
public void Serverdemo() throws IOException {
// 获取通道,绑定主机和端口号
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 非阻塞
serverSocketChannel.configureBlocking(false);
// 创建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 绑定连接
serverSocketChannel.bind(new InetSocketAddress(8080));
// 获取selector选择器
Selector sl = Selector.open();
// 通道注册到选择器,并进行监听
serverSocketChannel.register(sl, SelectionKey.OP_ACCEPT );
// 选择器进行轮询
while (sl.select() > 0) {
Set<SelectionKey> selectionKeys = sl.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
// 判断何种类型操作
if (next.isAcceptable()) {
// 获取连接
SocketChannel accpet = serverSocketChannel.accept();
//切换非阻塞模式
accpet.configureBlocking(false);
// 注册
accpet.register(sl, SelectionKey.OP_READ);
} else if (next.isReadable()) {
SocketChannel socketChannel = (SocketChannel) next.channel();
// 读取数据
int length = 0;
while ((length = socketChannel.read(byteBuffer)) > 0) {
byteBuffer.flip();
System.out.println("receiver" + new String(byteBuffer.array(), 0, length));
}
}
}
iterator.remove();
}
}
}
原文地址:https://blog.csdn.net/qq_22152499/article/details/136012354
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_66249.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!