java nio socket_如何正确关闭Java NIO中的SocketChannel?

本文探讨了在Java NIO中遇到的`CancelledKeyException`问题,该问题发生在尝试关闭SocketChannel后,主循环在检测写入事件时仍尝试使用已关闭的通道。解决方案包括在检查通道是否可写之前验证其有效性,或者确保每次只对一个事件感兴趣以避免Selector循环不必要的旋转。

我有一个简单的非阻塞服务器与主循环:

try {

while (selector.select() > -1) {

// Wait for an event one of the registered channels

// Iterate over the set of keys for which events are available

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

while (selectedKeys.hasNext()) {

SelectionKey key = (SelectionKey) selectedKeys.next();

selectedKeys.remove();

try {

if (!key.isValid()) {

continue;

}

if (key.isConnectable()) {

connect(key);

}

// Check what event is available and deal with it

if (key.isAcceptable()) {

accept(key);

}

if (key.isReadable()) {

read(key);

}

if (key.isWritable()) {

write(key);

}

} catch (Exception e) {

e.printStackTrace();

close(key);

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

在读/写部分,我检查是否有东西要读/写 – 如果没有 – 然后我尝试关闭通道:

if (channel.read(attachment.buffer) < 1)

close(key);

关闭方法:

private void close(SelectionKey key) throws IOException {

key.cancel();

key.channel().close();

}

但是在处理这段代码的过程中,我在主循环中得到了异常(它被捕获但我认为有些错误)我得到了这个堆栈跟踪:

java.nio.channels.CancelledKeyException

at sun.nio.ch.SelectionKeyImpl.ensureValid(Unknown Source)

at sun.nio.ch.SelectionKeyImpl.readyOps(Unknown Source)

at java.nio.channels.SelectionKey.isWritable(Unknown Source)

因此,当进入写入部分,关闭通道并在“可写”部分中返回到主循环时,它在主循环上失败,并且失败并出现此类异常.有什么建议?

解决方法:

错误很简单.

if (!key.isValid()) {

continue;

}

if (key.isConnectable()) {

connect(key);

}

// Check what event is available and deal with it

if (key.isAcceptable()) {

accept(key);

}

if (key.isReadable()) {

read(key);

}

if (key.isWritable()) {

write(key);

}

您的read方法是取消SelectionKey的方法.但是,从读取返回后,您再次测试该通道是否可写的密钥 – 可能只需取消相同的密钥!您的初步检查在这里无济于事.

一种解决方案是检查密钥在可能被取消的任何地方是否有效:

...

if (key.isValid() && key.isWritable()) {

write(key);

}

...

或者,您也可以尝试在任何特定频道上一次只注册一个兴趣,因此所有准备事件都是互斥的:

if (!key.isValid()) {

continue;

}

if (key.isConnectable()) {

connect(key);

} else if (key.isAcceptable()) {

accept(key);

} else if (key.isReadable()) {

read(key);

} else if (key.isWritable()) {

write(key);

}

这在情况下可能是有益的;因为通常一个通道几乎总是可以写入就绪,保持对沿着读取就绪的写入准备感兴趣可能使Selector循环保持旋转,这很可能是不可取的.在大多数情况下,通常仅在底层套接字输出缓冲区已满时才注册写入就绪感兴趣.

作为附注,知道SocketChannel.read可以返回值< 1没有它是一个错误.

A read operation might not fill the buffer, and in fact it might not read any bytes at all. Whether or not it does so depends upon the nature and state of the channel. A socket channel in non-blocking mode, for example, cannot read any more bytes than are immediately available from the socket’s input buffer;

另外,Selector.select没有说明返回< -1表示它已关闭.

Returns: The number of keys, possibly zero, whose ready-operation sets were updated

标签:java,nio,socketchannel

来源: https://codeday.me/bug/20190723/1512162.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值