我正在使用java.nio编写套接字服务器。因为我需要我的服务器使用0个线程我使用java.nio.channels.Selector
。我的代码如下所示。
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) { // Accept client connections
this.acceptClient(key);
} else if (key.isReadable()) { // Read from client
this.read(key);
} else if (key.isWritable()) {
this.write(key);
}
}
private void acceptClient(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
SocketAddress clientAddress= channel.getRemoteAddress();
//clients is a Hashmap
clients.put(clientAddress, new Client());
clientConnected(clientAddress.toString());
System.out.println("Connected to: " + clientAddress);
channel.register(this.selector, SelectionKey.OP_READ);
}
正如您所看到的,我正在为每个接受的客户端创建一个新的Client对象。我需要做的是,相关的Client对象来处理自己的读写。
我的方法是用他们的地址唯一地识别客户并将其转发给相关的客户对象
我认为使用客户端地址来唯一标识客户端并不是一个好方法。处理这个问题的最佳方法是什么?
答案 0 :(得分:1)
当您使用Channel
注册Selector
时:
channel.register(this.selector, SelectionKey.OP_READ);
它会返回SelectionKey
,您可以在以后从Selector
中选择时使用。{/ p>
使用该密钥填充IdentityHashMap<SelectionKey,Client>
,以便将IO定向到正确的Client
实例。正如EJP在他的回答中指出的那样,如果没有正确清理,这将泄漏SelectionKeys。如果您不想清除它们,也可以使用WeakHashMap
,但是您依赖隐式行为而不是选择器的显式行为。
EJP建议使用附件可能是最好的选择。虽然我可以想象一个更复杂的场景,你可能想要保留额外的附件,并且可能重构附件是一个封装成本很高。
答案 1 :(得分:1)
我认为使用客户端地址来唯一地识别客户端并不是一个好方法。
没有错。 TCP / IP的语义保证每个接受的套接字都有不同的远程SocketAddress
。
但你不需要它,或Map
。只需将Client
保存为SelectionKey
的附件即可。这样,当您关闭Client
时,SelectionKey
也会自动与Channel
一起消失。
相比之下,根据其他地方的建议更改为IdentityHashMap<SelectionKey, Client>
会让您有机会泄漏SelectionKey
,因此泄露Channel
和Client
。