簡體   English   中英

我可以使用Java NIO從Socket讀取可用字節嗎?

[英]can I get available bytes to read from a Socket using Java NIO?

我想知道在Java NIO套接字中是否有可讀字節。 在C / C ++中,可以使用以下內容完成:

int available(int fd){
  long readable = 0;

    if (ioctl(fd, FIONREAD, &readable) < 0){
      // error
    }

  return (int) readable;
}

可以使用Java NIO (使用SocketChannelSelectorSocket等)執行相同的操作嗎?

無法做到。 正式來說,API通過SocketChannel.socket().getInputStream().available() ,但getInputStream()操作將在非阻塞通道上失敗,因此它不能在您的環境中使用。

編輯:既然你已經照亮了我們一點點,你所需要的東西在Java中仍然不存在,但是當你處於非阻塞模式時,它無關緊要。 只需讀入一個至少與套接字接收緩沖區一樣大的緩沖區:讀取的字節數是可以在不阻塞的情況下讀取的字節數,這正是您剛才所做的。

NIO背后的想法是提供一種方法來同時從多個連接中的任何一個連接等待事件(例如“連接已准備好可讀數據”),這聽起來就像您正在尋找的那樣。 看看java.nio.channels.Selector 您基本上使用此選擇器對象注冊所有連接的“可讀”事件,然后調用將等待其中一個連接上的事件的三個select方法之一:

  1. select() - 阻塞直到事件可用(如果你的程序沒有別的事情可以使用)
  2. select(long timeout) - 阻塞直到事件可用或發生超時(如果你想節省CPU負載並提高網絡響應性,如果你的程序運行速度慢了就可以使用)
  3. selectNow() - 立即返回(如果你的程序需要繼續運行,請使用)

然后使用selectedKeys方法獲取具有等待事件的所有連接的列表(例如,准備好讀取數據)。 然后只需遍歷此列表並從僅包含數據的連接中讀取,並忽略列表中沒有的其他連接,因為它們沒有可用的數據。

這將允許您無阻塞地檢查數據是否可用(列表中是否有連接),但不能獲得多少數據。 但是,如果您隨后讀取有關可用數據的連接,它將立即返回而不會阻塞,並返回盡可能多的數據,如果您已經給它足夠大的緩沖區。 然后,您可以選擇在某處緩沖此數據並使緩沖區中的數據量可用,但有些事情告訴我您不需要這樣做,只是想繼續處理數據。

我已經閱讀了大量文檔,論壇以及您的每個回復。 我開發並測試了一個解決方案。 它不是最好的,但它是我最好的近似值。

我使用Selector的 selectNow()方法。 給定一個名為selector已注冊SocketChannelSelector ,我可以使用此方法來了解關聯的Socket是否具有可讀字節:

public boolean isReadable() {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while(keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if(!selectionKey.isValid()) {
                continue;
            }
            if(selectionKey.isReadable()) {
                return true;
            }
            keyIterator.remove();
        }
    } catch(IOException e) {
        // error
    }
    return false;
}

這是我發現知道Socket是否具有可讀字節而不讀取字節的唯一方法。 您是否知道更好的方法或通過這種方式做到這一點的缺點?

編輯:

這個解決方案第一次工作,但它有一個缺點:即使0字節是可讀的,它總是返回true。 即使沒有可用的數據,套接字也是可讀的。 這不是我想要的行為,因為當讀緩沖區中沒有數據時我不想讀取。

EDIT2 BIS:

測試此代碼:

public static boolean isReadable(Selector selector) {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if (!selectionKey.isValid()) {
                continue;
            }
            if (selectionKey.isReadable()) {
                    keyIterator.remove();
                return true;
            }
            keyIterator.remove();
        }
    } catch (IOException e) {
        System.err.println("Error!");
        e.printStackTrace();
    }
    return false;
}

public static void read() {
    try {
        Selector selector = Selector.open();
        SocketChannel socketChannel = SocketChannel.open();

        Socket socket = socketChannel.socket();

        System.out.println("Connecting to server...");
        socket.connect(new InetSocketAddress("localhost", 9999));
        System.out.println("Connected to server.");

        socketChannel.configureBlocking(false);

        socketChannel.register(selector, SelectionKey.OP_READ);

        ByteBuffer bb = ByteBuffer.allocate(1024);

        while (true) {
            boolean readable = isReadable(selector);
            if (readable) {
                System.out.println("Readable data found! Let's read...");
                bb.clear();
                int readBytes = socketChannel.read(bb);
                if (readBytes < 0) {
                    System.out.println("End of Stream found");
                    break;
                }
                byte[] readArray = new byte[readBytes];
                System.arraycopy(bb.array(), 0, readArray, 0, readBytes);
                System.out.println("Read (" + (readBytes) + " bytes): "
                        + new String(readArray, Charset.forName("UTF-8")));
                bb.flip();
                socketChannel.write(bb);
                Thread.sleep(1000);
            } else {
                System.out.println("No data to read, sleeping");
                Thread.sleep(1000);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    read();
}

首先,我執行一個監聽連接和寫入數據的應用程序:我使用netcat和這個命令: nc -l 9999 然后我執行我的Java程序。 最后,我可以在netcat編寫文本行,我的Java程序也能完美運行。

請記住,它是一個概念證明,而不是真正的編程解決方案。 事實上,這是一個非常糟糕的做法。 謝謝@EJP和@Markus!

我不知道NIO中有任何方法可以做到這一點。 一個人就去吧。

Bytebuffer.limit將返回緩沖區中可用的長度。

sctpChannel.receive(byteBuffer, null, null);
byteBuffer.flip();

 if (byteBuffer.limit() > 0) 
  {
         .....................//Do your work here
    //             read the data in a byte array
    byteBuffer.clear();  
  }

暫無
暫無

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

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