【发布时间】:2015-07-30 22:50:02
【问题描述】:
所以我的 ServerRunnable 类中有以下代码:
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() 只返回准备好操作的键是正确的吗?如果是这种情况,我如何检查未正确关闭的连接?我可以在这个 ServerRunnable while(running){} 循环中做到这一点吗?
我一直在考虑的一个选项是将密钥本身的引用附加到 CommunicationState 机器,然后在该过程完成后我可以获得对通道的引用并将其关闭。但出于某种原因,我对这个解决方案感到不安,我觉得它不合适。
如果是包含关闭的频道密钥的情况,我可以使用key.isValid() 确认需要永久删除该密钥吗?
如果您对这个过程有任何想法,我将不胜感激,我一定忽略了一些事情。
编辑:快速测试似乎表明通道键不包含在所选键集中,除非它们已准备好用于三个定义的操作之一 我的测试很糟糕。
【问题讨论】:
-
重新编辑,当然是这样。 selected-keys 集是为其注册的操作选择的键集。
-
是的,我知道,我在这里抓住了稻草! :-) 回到有条不紊的方法,就是这样。
-
我将再次尝试单步执行代码。我有一种情况,该过程正在完成,客户端成功重新启动,但我机器上的处理器保持在 30% - 40% 标记之间,就好像它在继续循环一样,这是我能说的最好的。我的
readFromSocketChannel()方法似乎没有被调用。我已经记录输出读取的字节,并说明通道何时关闭,但这些都没有被打印。 -
好的,我找到了问题,我的状态机处于错误的状态,所以它无法调用
readFromSocketChannel()方法。感谢您的耐心等待,您的帮助很大! -
经过一些调试后发现状态是正确的。由于我不明白
-1将在byteBuffer本身中返回,而不是在int read = socketChannel.read(byteBuffer);返回的值中,导致该错误发生
标签: java multithreading server nio