簡體   English   中英

如何避免使用Java ServerSocket阻塞?

[英]How can I avoid blocking with Java ServerSocket?

我正在研究一個套接字監聽器,它必須在2個端口上偵聽2種類型的數據(端口80和端口81)。 這些數據與對數據執行的操作類型非常相似,只是因為它們到達不同的端口而不同。 我繼續使用Java的ServerSocket類編寫實現,但后來才意識到ServerSocket類的accept()方法是塊,我的實現無法承受。 所以現在我正在考慮使用Java NIO實現相同的功能,但在完成了一些教程之后,我認為我比我開始時更困惑。 如果這里的某個人可以引導我完成整個過程,即使它是偽代碼或技術“下一步該做什么”,那將是很棒的。 這就是我打算實現的目標。

通過調用2個類似的線程來監聽,就像永遠在2個端口上一樣。(非阻塞)來自某個網​​絡位置的遠程設備連接,發送數據然后斷開連接。

我想如果只知道如何使用NIO設置服務器來監聽端口,那么就可以實現localhost上的端口80,其余的都很容易實現。

干杯

這里有一個開始使用NIO的小例子。

它是一個偵聽端口80和81的服務器,並打印標准輸出上接收的所有內容。 收到以CLOSE開頭的數據包后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;
            }
        }
    }
}

ObsSelectionKeyOP_ACCEPT ...)的字段是靜態導入的:

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

當您需要擴展到數千個同時連接時,需要NIO。

否則,我建議使用多個線程。 對於每個端口(及其對應的ServerSocket ),創建一個在循環中調用accept()的線程。 這些調用會阻塞,但這沒關系,因為其他線程正在運行,負責任何可用的任務。

接受新Socket ,創建專用於該連接的另一個線程。 它取決於應用程序,但通常此線程將從套接字讀取(阻塞操作),並執行請求的操作,將結果寫回套接字。

在大多數桌面平台上,此體系結構將擴展到數百個連接。 編程模型非常簡單,只要每個連接都是獨立的並且獨立於其他連接(這可以避免並發問題)。 引入NIO將提供更多可擴展性,但需要很多復雜性。

許多框架,例如Apache MINANetty ,都是基於Java NIO實現的,以增強非阻塞IO編程。 我強烈建議他們讓你的NIO編程變得快樂,而不是噩夢。 他們適合你的問題。

此外,嘗試在傳輸消息大小和編碼/解碼(序列化/反序列化)性能中使用有效的協議。 Google Protocol Buffers是該領域的可靠解決方案。 還可以看看KryoKryoNet 他們可以提供幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM