繁体   English   中英

如何使accept() 函数非阻塞?

[英]How to make the accept() function non-blocking?

我的服务器正在侦听 2 个端口,它应该同时在每个端口上执行单独的功能。

我的问题是,服务器阻塞,直到第一个端口的客户端首先连接。
例如:如果第二个客户端在客户端连接到第一个端口之前尝试连接到第二个端口,则不会让它连接。

我创建了 2 个扩展到线程类的类,因此它们应该并行等待任何客户端,而不是阻塞它们之后的内容。 但它似乎并没有像我期望的那样工作。

public static void main(String[] args) throws Exception {

        System.out.println("server is running.");
        int clientNumber = 0;
        ServerSocket listenerTrans = new ServerSocket(9899);
        ServerSocket listenerDeter = new ServerSocket(9898);
        try {
            while (true) {


                new Deteriment(listenerDeter.accept(), clientNumber++).start();
                new Transpose(listenerTrans.accept(), clientNumber++).start();
            }

        } finally {
            listenerTrans.close();
            listenerDeter.close();
        }
    }

Deteriment 和 Transpose 是我扩展到线程类的类。

我希望 listenerDeter.accept() 不阻塞 listenerTrans.accept(),我希望线程的 accept() 两个并行发生。 另外为什么它不在我的代码中并行发生?

答案是使用ServerSocketChannelSelector Selector允许您的应用程序使用单个线程在多个通道上多路复用 I/O。 它可以在时钟或非阻塞模式下使用

这是一个示例(借自How java nio ServerSocketChannel accept works?并适合您的用例):

// Create the 2 server socket channels
ServerSocketChannel server1 = ServerSocketChannel.open();
ServerSocketChannel server2 = ServerSocketChannel.open();
// Configure channels for nonblocking I/O
server1.configureBlocking(false);
server2.configureBlocking(false);
// Bind channels' IP and port
server1.socket().bind(new java.net.InetSocketAddress(host, 9899));
server2.socket().bind(new java.net.InetSocketAddress(host, 9898));
// Create the selector
Selector selector = Selector.open();
// Register channels to selector (type OP_ACCEPT)
SelectionKey key1 = server1.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey key2 = server2.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select(); // blocks until one or more of the registered channels 
                       // has actionable I/O
    Iterator it = selector.selectedKeys().iterator();
    while (it.hasNext()) {
        SelectionKey selKey = (SelectionKey) it.next();
        if (selKey.isAcceptable()) {
            ServerSocketChannel ssc = (ServerSocketChannel) selKey.channel();
            SocketChannel sc = ssc.accept();
            if (selKey.equals(key1)) {
                new Deteriment(sc.socket() ...).start();
            } else {
                new Transpose(sc.socket(), ...).start();
            }
        }
    }
}

(注意事项:1:未测试,2:可能更优雅,3:可能的资源泄漏,4:您确实应该使用线程池/执行程序而不是手动触发新线程)

所以,首先,如果你喜欢它是异步的,你需要为你声明的每个 ServerSocket 使用单独的线程。 为什么? 因为概念 java.net 阻止了处理 net 认为的不可扩展的方式。 如果你喜欢它是非阻塞的,更具可扩展性,但更抽象(我的意思是你将分配缓冲区 ^ ^),你应该寻找 java nio。 **编辑:**我稍微修改一下你的代码它应该可以完成工作但它可以改进我的意思是它不是最优雅的版本^ ^

public static void main(String[] args) throws Exception {
    System.out.println("server is running.");
    final int[] clientNumber = {0};
    ServerSocket listenerTrans = new ServerSocket(9899);
    ServerSocket listenerDeter = new ServerSocket(9898);
    try {
        ExecutorService ex = Executors
            .newFixedThreadPool(2);
            ex.execute(
                () -> {
                    try {
                        Socket s = listenerDeter.accept();
                        new Deteriment(s, clientNumber[0]++).start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            );
            ex.execute(
                () -> {
                    try {
                        Socket s = listenerDeter.accept();
                        new Transpose(s, clientNumber[0]++).start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            );
    } finally {
        //listenerTrans.close();
        //listenerDeter.close();
    }
}  

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM