簡體   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