java interestops_Java NIO 基础四 选择器

从最基础的层面上来看,选择器提供了问询通道是否就绪操作I/O的能力,选择器可以监控注册在上面的多个通道,通道注册时会返回选择键(记录通道与选择器之间的关联关系),选择器管理者这些注册的键、和就绪状态键的集合

SelectableChannel

所有继承SelectableChannel的通道都可以在选择器中注册,FileChannel没有继承这个类,所以无法使用选择器

选择键(SelectionKey) 选择键是选择器的重点内容,选择器就绪的通道通过返回选择键集合来通知public abstract class SelectionKey {

public static final int OP_READ

public static final int OP_WRITE

public static final int OP_CONNECT

public static final int OP_ACCEPT

public abstract SelectableChannel channel();

public abstract Selector selector();

public abstract void cancel();

public abstract boolean isValid();

public abstract int interestOps();

public abstract void interestOps(int ops);

public abstract int readyOps();

public final boolean isReadable()

public final boolean isWritable()

public final boolean isConnectable()

public final boolean isAcceptable()

public final Object attach(Object ob)

public final Object attachment()

}

选择键维护了通道和选择器之间的关联,可以通过选择键获取Channel或Selector,键对象表示一种特殊的关联关系,当这种关系需要终止时,可以调用cancel()方法取消,调用这个方法时,不会立即被取消,而是将这个键放到被取消的集合里,当Selector下次调用select()方法时会真正被清理掉。当通道关闭时,选择键会自动被取消,当选择器关闭时,所有键都会被清理掉。

一个选择器键包含有两个准备好的操作集合,包括感兴趣的事件集合instrest和就绪的操作集合ready,通过掩码保存

感兴趣的事件集合interestOps()

通常一个键的instrest注册时就已经确认,但是可以在注册后通过interestOps(newOps)传入一个新的ops来改变这个值channel.register(this.selector, SelectionKey.OP_READ);

上面的代码注册的键interest包含read事件,可以在对通道IO异步处理时,改变这个ops来临时取消对read事件的关注,以防止重复处理未处理完的通道

就绪的操作集合readyOps()

通过这个方法返回就绪的操作,isReadable( ),isWritable( ),isConnectable( ), 和isAcceptable( )用来判断这些操作是否就绪,进行下一步的处理

下面列举两个示例来示范选择器的使用

单选择器单线程public abstract class AbstractNioServer {

protected final static String CHARSET = "utf-8";

protected String ip;

protected Integer port;

protected Selector selector;

public AbstractNioServer(String ip, Integer port) {

this.ip = ip;

this.port = port;

}

/**

* 客户端连接请求

*

* @param key

*/

protected abstract void accept(SelectionKey key) throws IOException;

/**

* 读取数据

*

* @param key

*/

protected abstract void read(SelectionKey key) throws IOException;

/**

* 初始化服务器

*

* @throws IOException

*/ public void init() throws IOException {

//设置服务器地址端口

SocketAddress address = new InetSocketAddress(this.ip, this.port);

//创建服务端通道

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//绑定服务器地址

serverSocketChannel.bind(address);

//设置为非阻塞模式

serverSocketChannel.configureBlocking(false);

//创建一个选择器

this.selector = Selector.open();

//将服务器通道注册到选择器中,ServerSocketChannel只支持accept事件注册,validOps返回16

serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);

}

public void start() throws IOException {

this.init();

while (true) {

int count = this.selector.select();

if (count == 0) {

//没有就绪的选择键

continue;

}

Iterator iterator = this.selector.selectedKeys().iterator();

while (iterator.hasNext()) {

SelectionKey key = iterator.next();

if (!key.isValid()) {

continue;

}

if (key.isAcceptable()) {

//连接请求

accept(key);

} else if (key.isReadable()) {

//修改键的感兴趣事件,防止被 select 重复调用,处理完事件后及时恢复

key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));

//读取消息

read(key);

}

iterator.remove();

}

}

}

/**

* 恢复键的感兴趣事件

* @param key

*/

protected void resumeInterOpsRead(SelectionKey key) {

//还原key的感兴趣事件

key.interestOps(key.interestOps() | SelectionKey.OP_READ);

//唤醒selector的select事件

key.selector().wakeup();

}

}public class SingleNioServer extends AbstractNioServer {

public static void main(String[] args) {

SingleNioServer server = new SingleNioServer("127.0.0.1", 1008);

try {

server.start();

} catch (IOException e) {

e.printStackTrace();

}

}

public SingleNioServer(String ip, Integer port) {

super(ip, port);

}

@Override

protected void accept(SelectionKey key) throws IOException {

ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();

//SocketChannel支持 read、write、connect 事件注册,validOps返回13=1+4+8

SocketChannel channel = serverChannel.accept();

if (channel == null) {

return;

}

System.out.println("新的连接请求");

channel.configureBlocking(false);

//如果是阻塞通道进行注册,会抛出 IllegalBlockingModeException 异常

channel.register(this.selector, SelectionKey.OP_READ);

}

@Override

protected void read(SelectionKey key) throws IOException {

SocketChannel channel = (SocketChannel) key.channel();

try {

ByteBuffer buffer = ByteBuffer.allocate(1024);

int len = channel.read(buffer);

buffer.flip();

if (len > 0) {

String str = Charset.forName(CHARSET).decode(buffer).toString();

System.out.println("客户端消息:" + str);

String msg = "消息已收到";

byte[] sendData = msg.getBytes(CHARSET);

ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);

channel.write(sendBuffer);

super.resumeInterOpsRead(key);

} else if (len == -1) {

System.out.println("socket client close");

key.cancel();

channel.close();

}

} catch (IOException ex) {

key.cancel();

channel.close();

}

}

}

单选择器多线程public class MulitpleNioServer extends AbstractNioServer {

public static void main(String[] args) throws IOException {

MulitpleNioServer server = new MulitpleNioServer("127.0.0.1", 1008);

server.start();

}

/**

* 线程池

*/

private ExecutorService executorService = Executors.newFixedThreadPool(5);

public MulitpleNioServer(String ip, Integer port) {

super(ip, port);

}

@Override

protected void accept(SelectionKey key) throws IOException {

ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();

//SocketChannel支持 read、write、connect 事件注册,validOps返回13=1+4+8

SocketChannel channel = serverChannel.accept();

System.out.println("新的连接请求");

channel.configureBlocking(false);

channel.register(this.selector, SelectionKey.OP_READ);

}

@Override

protected void read(SelectionKey key) throws IOException {

executorService.submit(new Runnable() {

@Override

public void run() {

readData(key);

}

});

}

private void readData(SelectionKey key) {

SocketChannel channel = (SocketChannel) key.channel();

try {

ByteBuffer buffer = ByteBuffer.allocate(1024);

if (channel.isOpen()) {

if (channel.isConnected()) {

int len = channel.read(buffer);

buffer.flip();

if (len > 0) {

String str = Charset.forName(CHARSET).decode(buffer).toString();

System.out.println("客户端消息:" + str);

String msg = "消息已收到";

byte[] sendData = msg.getBytes(CHARSET);

ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);

channel.write(sendBuffer);

} else if (len == -1) {

System.out.println("socket client close1");

key.cancel();

channel.close();

}

super.resumeInterOpsRead(key);

}

}

} catch (IOException ex) {

System.out.println("client is close2");

key.cancel();

try {

channel.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java NIO中,可以通过SelectionKey对象的interestOps()方法来监控Channel的状态。 首先,您需要创建一个Selector对象,并将其注册到您要监控的Channel上。然后,使用Selector.select()方法来阻塞等待已注册的Channel上发生事件。当有事件发生时,select()方法将返回,并返回一个SelectionKey集合,您可以通过这个集合来获取发生事件的Channel对象。接下来,您可以使用SelectionKey对象的interestOps()方法来检查Channel的状态。 例如,以下代码演示了如何使用Selector来监控Channel的可读状态: ``` Selector selector = Selector.open(); channel.register(selector, SelectionKey.OP_READ); while (true) { selector.select(); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isReadable()) { // Channel is ready for reading // Do something... } keyIterator.remove(); } } ``` 在这个示例中,我们首先将Channel注册到Selector上,指定我们要监控Channel的可读状态。然后,在一个无限循环中,我们使用Selector.select()方法等待Channel上发生事件,当有事件发生时,我们遍历SelectionKey集合,检查哪些Channel是可读的。 当我们发现可读的Channel时,我们可以在其中执行读操作,并在操作完成后将SelectionKey从集合中删除。这样,我们就可以在控制台监控Channel的状态了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值