NIO Selector Thread,按预期处理通道,但如何确保通道在使用后正确关闭?
NIO Selector Thread, handling channels as expected, but how do I ensure channels are correctly closed after use?
所以我的 ServerRunnable class 中有以下代码:
public class FirmwareServerRunnable implements Runnable {
private static Logger log = Logger.getLogger(FirmwareServerRunnable.class
.getName());
private LinkedTransferQueue<CommunicationState> communicationQueue;
private int serverPort = 48485;
public FirmwareServerRunnable(int port,
LinkedTransferQueue<CommunicationState> communicationQueue) {
serverPort = port;
this.communicationQueue = communicationQueue;
}
private boolean running;
private ServerSocketChannel serverSocketChannel;
@Override
public void run() {
try {
Selector selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(serverPort));
log.info("Selector Thread: FirmwareServer Runnable- Listening for connections on port: "
+ serverSocket.getLocalPort());
running = true;
@SuppressWarnings("unused")
SelectionKey serverAcceptKey = serverSocketChannel.register(
selector, SelectionKey.OP_ACCEPT);
while (running) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
acceptConnection(selector, key);
keyIterator.remove();
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
CommunicationState commsState = (CommunicationState) key
.attachment();
if (commsState.getCurrentState() == CommunicationState.STATE_READ) {
readFromSocketChannel(key);
keyIterator.remove();
}
} else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
CommunicationState commsState = (CommunicationState) key
.attachment();
if (commsState.getCurrentState() == CommunicationState.STATE_WRITE) {
writeToSocketChannel(key);
keyIterator.remove();
}
}
}
}
} catch (IOException e) {
log.error(
"Firmware Selector Thread: An IOException occurred",
e);
}
}
我的 acceptConnection()
方法接受一个连接并向其中添加一个 CommunicationState
对象(状态机),其中包含诸如 ByteBuffer
、当前通道状态之类的内容,其中客户端当前正在沟通过程中等...
该服务器在进程中间的通信方法之间切换。最初它使用 JSON 消息与客户端通信,但是当它到达某个点时它开始使用 USART protocol commands 使用新固件刷新客户端。
该过程完成后,客户端将断开连接并重新启动。这使我的频道处于未知状态。我不确定频道是否已关闭。
我怎样才能检查这个?我是否认为 selector.selectedKeys()
只有 returns 键可以操作?如果是这样,我如何检查未正确关闭的连接?我可以在 ServerRunnable
while(running){}
循环中这样做吗?
我一直在考虑的一个选项是将对密钥本身的引用附加到 CommunicationState 机器,然后我可以在该过程完成后获得对通道的引用并在那里关闭它。但出于某种原因,我对这个解决方案感到不安,我觉得它不合适。
如果连关闭的频道密钥都包含在内,我可以使用 key.isValid()
来确认需要永久删除该密钥吗?
如果您对此过程有任何想法,我将不胜感激,我一定是忽略了一些东西。
编辑:快速测试似乎表明通道键不包含在所选键集中,除非它们已准备好进行三个定义的操作之一
我的考试很糟糕。
已被对等方关闭的连接将导致选择器将您的通道视为可读,并且当您从中读取时,您将得到 -1,此时您应该关闭通道,这将取消其选择键。
编辑
If it's the case that even closed channel keys are included can I use key.isValid() to confirm that the key needs to be removed permanently?
如果你关闭了频道,它的密钥就被取消了,所以你不会在下次选择的密钥集中看到它。如果 peer 关闭了 连接, 见上文。
所以我的 ServerRunnable class 中有以下代码:
public class FirmwareServerRunnable implements Runnable {
private static Logger log = Logger.getLogger(FirmwareServerRunnable.class
.getName());
private LinkedTransferQueue<CommunicationState> communicationQueue;
private int serverPort = 48485;
public FirmwareServerRunnable(int port,
LinkedTransferQueue<CommunicationState> communicationQueue) {
serverPort = port;
this.communicationQueue = communicationQueue;
}
private boolean running;
private ServerSocketChannel serverSocketChannel;
@Override
public void run() {
try {
Selector selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(serverPort));
log.info("Selector Thread: FirmwareServer Runnable- Listening for connections on port: "
+ serverSocket.getLocalPort());
running = true;
@SuppressWarnings("unused")
SelectionKey serverAcceptKey = serverSocketChannel.register(
selector, SelectionKey.OP_ACCEPT);
while (running) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
acceptConnection(selector, key);
keyIterator.remove();
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
CommunicationState commsState = (CommunicationState) key
.attachment();
if (commsState.getCurrentState() == CommunicationState.STATE_READ) {
readFromSocketChannel(key);
keyIterator.remove();
}
} else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
CommunicationState commsState = (CommunicationState) key
.attachment();
if (commsState.getCurrentState() == CommunicationState.STATE_WRITE) {
writeToSocketChannel(key);
keyIterator.remove();
}
}
}
}
} catch (IOException e) {
log.error(
"Firmware Selector Thread: An IOException occurred",
e);
}
}
我的 acceptConnection()
方法接受一个连接并向其中添加一个 CommunicationState
对象(状态机),其中包含诸如 ByteBuffer
、当前通道状态之类的内容,其中客户端当前正在沟通过程中等...
该服务器在进程中间的通信方法之间切换。最初它使用 JSON 消息与客户端通信,但是当它到达某个点时它开始使用 USART protocol commands 使用新固件刷新客户端。
该过程完成后,客户端将断开连接并重新启动。这使我的频道处于未知状态。我不确定频道是否已关闭。
我怎样才能检查这个?我是否认为 selector.selectedKeys()
只有 returns 键可以操作?如果是这样,我如何检查未正确关闭的连接?我可以在 ServerRunnable
while(running){}
循环中这样做吗?
我一直在考虑的一个选项是将对密钥本身的引用附加到 CommunicationState 机器,然后我可以在该过程完成后获得对通道的引用并在那里关闭它。但出于某种原因,我对这个解决方案感到不安,我觉得它不合适。
如果连关闭的频道密钥都包含在内,我可以使用 key.isValid()
来确认需要永久删除该密钥吗?
如果您对此过程有任何想法,我将不胜感激,我一定是忽略了一些东西。
编辑:快速测试似乎表明通道键不包含在所选键集中,除非它们已准备好进行三个定义的操作之一 我的考试很糟糕。
已被对等方关闭的连接将导致选择器将您的通道视为可读,并且当您从中读取时,您将得到 -1,此时您应该关闭通道,这将取消其选择键。
编辑
If it's the case that even closed channel keys are included can I use key.isValid() to confirm that the key needs to be removed permanently?
如果你关闭了频道,它的密钥就被取消了,所以你不会在下次选择的密钥集中看到它。如果 peer 关闭了 连接, 见上文。