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

问题描述 投票:0回答:2

我的服务器正在侦听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();
        }
    }

Determinant和Transpose是扩展到线程类的类。

我希望listenerDeter.accept()不要阻塞listenerTrans.accept(),我希望两个线程的accept()并行发生。为什么我的代码并没有并行发生?

java networking java-threads
2个回答
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:你真的应该使用线程池/执行器而不是手动触发新线程)


0
投票

所以,首先如果你喜欢它是异步的,你需要为你声明的每个ServerSocket使用单独的线程。为什么?由概念java.net所阻挡的是不可扩展的处理网络思维方式。如果你喜欢它是非阻塞更可扩展但更少abstrac(我的意思是你将分配缓冲区^^),你应该寻找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();
    }
}  
© www.soinside.com 2019 - 2024. All rights reserved.