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

时间:2019-04-15 20:52:31

标签: java networking java-threads

我的服务器正在监听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()并行发生。 还有为什么它在我的代码中不是并行发生的?

2 个答案:

答案 0 :(得分:1)

答案是使用ServerSocketChannelSelectorSelector允许您的应用程序使用单个线程在多个通道上复用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:您确实应该使用线程池/执行程序,而不是手动触发新线程)

答案 1 :(得分:0)

因此,首先,如果您希望它异步,则需要为您声明的每个ServerSocket使用单独的线程。为什么?由于概念java.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();
    }
}