一线程一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的主要内容,如果未能解决你的问题,请参考以下文章

Netty学习一

Netty01—NIO,本文四万字教程带你入门netty,肝!连载中

Netty——ByteBuffer的内部结构

Netty——ByteBuffer的内部结构

Netty网络编程第一卷

Netty——ByteBuffer的基本使用