Java NIO SeletionKey 迭代器和键处理,我做对了吗?
Java NIO SeletionKey iterator and key handling, am I doing it right?
我正在关注 this tutorial on Java NIO. I found what appears to be the correct java file Here。我已经调整我的代码以在单个端口而不是多个端口上运行,并且只是将数据输出到屏幕而不是将其回显给客户端。
当我 运行 代码时,我的版本和上面 hyper-link 上的版本,似乎通道处理不当。我只是从我的笔记本电脑风扇的声音得出这个结论,在快速发送一些 System.out.println()
消息后,我设法缩小了问题的范围。起初我认为 selector.select()
方法没有因为某种原因阻塞,通道似乎是空的,但是 while(hasContent)
循环一直在迭代。
经过一番搜索,我找到了 this post,并意识到我可能没有正确处理密钥。从通道读取数据后,我调整了代码以取消 while(iterator.hasNext())
循环底部 if 语句中的 SelectionKey。
这似乎已经成功了,现在我收到一条消息,通知我在关闭通道之前没有更多的字节要读取,而且总体上 运行 似乎更顺利。
这是我到目前为止所做的代码,我对如何实现它的想法是否正确?我是否在正确的位置取消了密钥?
public class ServerRunnable implements Runnable {
private int serverPort;
private int queueLength;
public ServerRunnable(int serverPort, int queueLength) {
this.serverPort = serverPort;
this.queueLength = queueLength;
}
private boolean running;
private ServerSocketChannel serverSocketChannel;
private ByteBuffer buffer = ByteBuffer.allocate(1024);
@Override
public void run() {
try {
Selector selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(serverPort));
System.out
.println("DeviceServerV0.2 - Going to Listen for connections on port: "
+ serverSocket.getLocalPort());
running = true;
SelectionKey serverAcceptKey = serverSocketChannel.register(
selector, SelectionKey.OP_ACCEPT);
int count = 0;
while (running) {
int keyCount = selector.select();
System.out.println("keyCount: " + keyCount);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
ServerSocketChannel ssc = (ServerSocketChannel) key
.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// Here we add the new connection to the selector
SelectionKey newKey = sc.register(selector,
SelectionKey.OP_READ);
keyIterator.remove();
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
SocketChannel sc = (SocketChannel) key.channel();
// Going with this from the tutorial for now.
// TODO: Implement proper boolean controls here
while (true) {
buffer.clear();
int read = sc.read(buffer);
if (read <= 0) {
// System.out.println("Bytes read: " + read);
System.out
.println("No more bytes, breaking loop.");
break;
} else {
buffer.flip();
String result = new String(buffer.array());
System.out
.println("Buffer Contents: " + result);
}
}
keyIterator.remove();
key.cancel();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我也知道调用 ByteBuffer.array()
不会影响 byteBuffer 中的当前位置,因此它仍然看起来好像尚未读取缓冲区。我应该以某种方式处理这个问题还是足以调用 ByteBuffer.clear()
作为循环的开始?
我打算继续尝试构建一个使用线程池来处理请求的 NIO 服务器,但我想确保到目前为止我对 NIO 的理解是正确的。
当频道的readyOps()
包含SelectionKey.OP_READ
时,这意味着可以读取一些数据,而不是可以读取整个频道直到结束.但这就是你在 while(true)
循环中所做的;您正在阅读,因此轮询一个频道直到它结束。
正确的处理方法是读取一次并且不要尝试再次读取,除非下一次select
报告有更多的数据要读取。
我正在关注 this tutorial on Java NIO. I found what appears to be the correct java file Here。我已经调整我的代码以在单个端口而不是多个端口上运行,并且只是将数据输出到屏幕而不是将其回显给客户端。
当我 运行 代码时,我的版本和上面 hyper-link 上的版本,似乎通道处理不当。我只是从我的笔记本电脑风扇的声音得出这个结论,在快速发送一些 System.out.println()
消息后,我设法缩小了问题的范围。起初我认为 selector.select()
方法没有因为某种原因阻塞,通道似乎是空的,但是 while(hasContent)
循环一直在迭代。
经过一番搜索,我找到了 this post,并意识到我可能没有正确处理密钥。从通道读取数据后,我调整了代码以取消 while(iterator.hasNext())
循环底部 if 语句中的 SelectionKey。
这似乎已经成功了,现在我收到一条消息,通知我在关闭通道之前没有更多的字节要读取,而且总体上 运行 似乎更顺利。
这是我到目前为止所做的代码,我对如何实现它的想法是否正确?我是否在正确的位置取消了密钥?
public class ServerRunnable implements Runnable {
private int serverPort;
private int queueLength;
public ServerRunnable(int serverPort, int queueLength) {
this.serverPort = serverPort;
this.queueLength = queueLength;
}
private boolean running;
private ServerSocketChannel serverSocketChannel;
private ByteBuffer buffer = ByteBuffer.allocate(1024);
@Override
public void run() {
try {
Selector selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(serverPort));
System.out
.println("DeviceServerV0.2 - Going to Listen for connections on port: "
+ serverSocket.getLocalPort());
running = true;
SelectionKey serverAcceptKey = serverSocketChannel.register(
selector, SelectionKey.OP_ACCEPT);
int count = 0;
while (running) {
int keyCount = selector.select();
System.out.println("keyCount: " + keyCount);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
ServerSocketChannel ssc = (ServerSocketChannel) key
.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// Here we add the new connection to the selector
SelectionKey newKey = sc.register(selector,
SelectionKey.OP_READ);
keyIterator.remove();
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
SocketChannel sc = (SocketChannel) key.channel();
// Going with this from the tutorial for now.
// TODO: Implement proper boolean controls here
while (true) {
buffer.clear();
int read = sc.read(buffer);
if (read <= 0) {
// System.out.println("Bytes read: " + read);
System.out
.println("No more bytes, breaking loop.");
break;
} else {
buffer.flip();
String result = new String(buffer.array());
System.out
.println("Buffer Contents: " + result);
}
}
keyIterator.remove();
key.cancel();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我也知道调用 ByteBuffer.array()
不会影响 byteBuffer 中的当前位置,因此它仍然看起来好像尚未读取缓冲区。我应该以某种方式处理这个问题还是足以调用 ByteBuffer.clear()
作为循环的开始?
我打算继续尝试构建一个使用线程池来处理请求的 NIO 服务器,但我想确保到目前为止我对 NIO 的理解是正确的。
当频道的readyOps()
包含SelectionKey.OP_READ
时,这意味着可以读取一些数据,而不是可以读取整个频道直到结束.但这就是你在 while(true)
循环中所做的;您正在阅读,因此轮询一个频道直到它结束。
正确的处理方法是读取一次并且不要尝试再次读取,除非下一次select
报告有更多的数据要读取。