一线程一ByteBuffer NIO
Posted
技术标签:
【中文标题】一线程一ByteBuffer NIO【英文标题】:One thread one ByteBuffer NIO 【发布时间】:2015-06-15 02:51:41 【问题描述】:我想知道用一个线程和一个缓冲区实现多用户 NIO 服务器的最佳方法。目前我使用选择器来实现这一点,但我只知道如何在所有客户端上进行阅读。我在使用一个缓冲区实现写入时遇到问题。我需要第二个缓冲区进行写入吗?或者我必须(不幸的是)每个客户端都有一个写缓冲区?我写这个例子是为了轻松展示我是如何做事的,只是为了让你知道所有处理都发生在调度程序的线程中,我没有通过另一个线程与任何状态交互!
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ServerSocketChannel server = ServerSocketChannel.open();
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
Map<SelectionKey, SocketChannel> clients = new HashMap<>();
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
server.bind(new InetSocketAddress(43594));
scheduler.scheduleAtFixedRate(() ->
try
selector.selectNow();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext())
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable())
for (int i = 0; i < 16; i++)
SocketChannel client = server.accept();
if (client == null)
break;
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
clients.put(clientKey, client);
if (key.isReadable())
SocketChannel client = clients.get(key);
if (client != null)
buffer.clear();
client.read(buffer);
buffer.flip();
// do stuff with buffer
catch (IOException e)
e.printStackTrace();
, 600, 600, TimeUnit.MILLISECONDS);
【问题讨论】:
【参考方案1】:你不能。对于比回显服务器更重要的任何东西,每个通道至少需要一个缓冲区,可能需要两个(读取和写入)。否则,您可能无法处理部分读取请求或部分写入响应。
您可以通过SelectionKey
附件将缓冲区与通道相关联。
【讨论】:
你确定吗?我目前的实现适用于许多客户,尽管我没有处理部分读取或写入。您是否知道有关如何执行此操作的详尽示例?我想尽量减少我的应用程序中的垃圾创建。 您当前的实现“有效”因为您尚未处理部分读取或写入。在你开始实现它的那一刻,你会意识到每个通道需要一个或两个缓冲区来保存待处理的数据。这肯定很明显吗?缓冲区或通道不会强加任何 Gc 负载,因为它与连接一样长,并且在任何情况下,您的主要关注点必须是正确性。你可以在零时间内得到错误的答案。它没有相关性或兴趣。 感谢您的回复,一切顺利。我想汇集我的缓冲区以节省垃圾,有什么建议吗?我也质疑附件的效率。我意识到他们可以简化我的代码,并且可能(?)通过引入上下文来消除对我的客户地图的需求。我不是在做实时开发,尽管它是针对需要在少量硬件上运行数千个客户端的游戏。如果有帮助,系统会基于每 600 毫秒发送一次消息! 您基于什么理由质疑附件的效率?以上是关于一线程一ByteBuffer NIO的主要内容,如果未能解决你的问题,请参考以下文章