我编写了一个多线程游戏服务器应用程序,它使用NIO处理多个同时连接。不幸的是,一旦第一个用户连接,该服务器就会在一个核心上产生完整的 CPU 负载,即使该用户实际上并未发送或接收任何数据。
下面是我的网络处理线程的代码(为了便于阅读,缩写为基本部分)。该课程ClientHandler
是我自己的课程,为游戏机制进行网络抽象。以下示例中的所有其他类均来自java.nio
.
如您所见,它使用while(true)
循环。我的理论是,当一个键是可写的时,selector.select()
会立即返回并被clientHandler.writeToChannel()
调用。但是当处理程序返回而不写任何东西时,密钥将保持可写状态。然后立即再次调用 select 并立即返回。所以我忙着转。
有没有办法设计网络处理循环,只要clientHandlers没有数据发送,它就会休眠?请注意,低延迟对我的用例至关重要,因此当没有处理程序有数据时,我不能让它休眠任意数量的毫秒。
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
// wait for connections
while(true)
{
// Wait for next set of client connections
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> i = keys.iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
i.remove();
if (key.isAcceptable()) {
SocketChannel clientChannel = server.accept();
clientChannel.configureBlocking(false);
clientChannel.socket().setTcpNoDelay(true);
clientChannel.socket().setTrafficClass(IPTOS_LOWDELAY);
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ClientHandler clientHanlder = new ClientHandler(clientChannel);
clientKey.attach(clientHandler);
}
if (key.isReadable()) {
// get connection handler for this key and tell it to process data
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.readFromChannel();
}
if (key.isWritable()) {
// get connection handler and tell it to send any data it has cached
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.writeToChannel();
}
if (!key.isValid()) {
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.disconnect();
}
}
}