【问题标题】:How can I avoid blocking with Java ServerSocket?如何避免使用 Java ServerSocket 阻塞?
【发布时间】:2015-10-09 17:35:00
【问题描述】:

我正在开发一个套接字侦听器,该侦听器必须在 2 个端口上侦听 2 种类型的数据(端口 80 和端口 81)。这些数据与对数据执行的操作类型非常相似,只是不同,因为它们到达 n 个不同的端口。我继续使用 Java 的 ServerSocket 类编写了一个实现,后来才意识到 ServerSocket 类的 accept() 方法是阻塞的,而我的实现负担不起。所以现在我正在考虑使用 Java NIO 实现相同的功能,但是在阅读了一些教程之后,我认为我比我开始的方式更困惑。如果这里有人可以指导我完成整个过程,那将是很棒的,即使它是伪代码或只是技术性的“下一步做什么”。 这是我打算实现的目标。

通过调用 2 个类似的线程,在 2 个端口上永远聆听。(非阻塞) 来自某个网​​络位置的远程设备连接、发送数据然后断开连接。

我认为,如果仅了解如何使用 NIO 设置服务器以在 localhost 上的端口(例如端口 80)上侦听,那么其余的都非常容易实现。

干杯

【问题讨论】:

    标签: java sockets concurrency nonblocking


    【解决方案1】:

    这里有一个开始使用 NIO 的小例子。

    它是一个侦听端口 80 和 81 的服务器,并打印在标准输出上接收到的所有内容。收到以CLOSE开头的数据包后关闭连接;收到以QUIT 开头的数据包后,整个服务器关闭。缺少发送部分和错误处理可能会更好一些。 :-)

    public static void main() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        Selector selector = Selector.open();
    
        ServerSocketChannel server1 = ServerSocketChannel.open();
        server1.configureBlocking(false);
        server1.socket().bind(new InetSocketAddress(80));
        server1.register(selector, OP_ACCEPT);
    
        ServerSocketChannel server2 = ServerSocketChannel.open();
        server2.configureBlocking(false);
        server2.socket().bind(new InetSocketAddress(81));
        server2.register(selector, OP_ACCEPT);
    
        while (true) {
            selector.select();
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SocketChannel client;
                SelectionKey key = iter.next();
                iter.remove();
    
                switch (key.readyOps()) {
                    case OP_ACCEPT:
                        client = ((ServerSocketChannel) key.channel()).accept();
                        client.configureBlocking(false);
                        client.register(selector, OP_READ);
                        break;
                    case OP_READ:
                        client = (SocketChannel) key.channel();
                        buffer.clear();
                        if (client.read(buffer) != -1) {
                            buffer.flip();
                            String line = new String(buffer.array(), buffer.position(), buffer.remaining());
                            System.out.println(line);
                            if (line.startsWith("CLOSE")) {
                                client.close();
                            } else if (line.startsWith("QUIT")) {
                                for (SelectionKey k : selector.keys()) {
                                    k.cancel();
                                    k.channel().close();
                                }
                                selector.close();
                                return;
                            }
                        } else {
                            key.cancel();
                        }
                        break;
                    default:
                        System.out.println("unhandled " + key.readyOps());
                        break;
                }
            }
        }
    }
    

    ObsSelectionKey (OP_ACCEPT...) 的字段是静态导入的:

    import static java.nio.channels.SelectionKey.*;
    

    【讨论】:

      【解决方案2】:

      许多框架,例如Apache MINANetty,已经基于Java NIO 实现,以促进非阻塞IO 编程。我强烈推荐他们让你的 NIO 编程成为一种乐趣,而不是一场噩梦。他们适合你的问题。

      此外,尝试在传输消息大小和编码/解码(序列化/反序列化)性能方面使用有效的协议。 Google Protocol Buffers 是该领域的可靠解决方案。另请查看KryoKryoNet。它们会很有帮助。

      【讨论】:

        【解决方案3】:

        当您需要扩展到数千个同时连接时,就需要 NIO。

        否则,我建议使用多个线程。对于每个端口(及其对应的ServerSocket),创建一个在循环中调用accept() 的线程。这些调用会阻塞,但没关系,因为其他线程正在运行,负责处理任何可用任务。

        当接受新的Socket 时,创建另一个专用于该连接的线程。这取决于应用程序,但通常此线程将从套接字读取(阻塞操作),并执行请求的操作,将结果写回套接字。

        此架构将在大多数桌面平台上扩展到数百个连接。并且编程模型相当简单,只要每个连接都是自包含的并且独立于其他连接(这避免了并发问题)。引入 NIO 将提供更大的可扩展性,但需要很多复杂性。

        【讨论】:

        • 没有真正回答问题:socket listener using NIO
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-23
        • 1970-01-01
        • 2017-08-17
        • 1970-01-01
        • 2012-09-02
        • 1970-01-01
        • 2014-04-12
        相关资源
        最近更新 更多