5

JavaNIOソケットで読み取ることができるバイトがあるかどうかを知りたいです。C / C ++では、次のようなものを使用して実行できます。

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

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

  return (int) readable;
}

Java NIOで同じ操作を行うことは可能ですか(SocketChannelSelectorSocketなどを使用)?

4

5 に答える 5

7

できません。API は正式には を介し​​て存在しますがSocketChannel.socket().getInputStream().available()getInputStream()非ブロッキング チャネルでは操作が失敗するため、この状況では使用できません。

EDIT:あなたが私たちを少し照らしたので、あなたが必要とするものはJavaにはまだ存在しませんが、ノンブロッキングモードであるため、それは少しも問題ではありません. ソケットの受信バッファーと少なくとも同じ大きさのバッファーに読み込むだけです。読み取ったバイト数は、ブロックせずに読み取ることができたバイト数であり、まさに今行ったことです。

于 2012-10-03T10:43:37.887 に答える
6

NIO の背後にあるアイデアは、複数の接続のいずれかから同時にイベント (たとえば、「接続で読み取り可能なデータの準備ができました」) を待機する方法を提供することです。をご覧くださいjava.nio.channels.Selector。基本的に、このセレクター オブジェクトを使用してすべての接続の「読み取り可能な」イベントを登録し、接続のselect1 つでイベントを待機する 3 つのメソッドのいずれかを呼び出します。

  1. select()- イベントが利用可能になるまでブロックします (プログラムで他に何もすることがない場合に使用します)
  2. select(long timeout)- イベントが利用可能になるか、タイムアウトが発生するまでブロックします (CPU 負荷を節約し、ネットワークの応答性を高めたい場合に使用します。プログラムが少し遅くなっても問題ありません)。
  3. selectNow()- すぐに戻ります (プログラムを実行し続ける必要がある場合に使用します)

次に、selectedKeysメソッドを使用して、イベントを待機しているすべての接続のリストを取得します (たとえば、データを読み取る準備ができている場合)。次に、このリストを反復処理し、データのみを含む接続から読み取り、リストにない他の接続は使用可能なデータがないため無視します。

これにより、データが利用可能かどうか (接続がリストにあるかどうか) をブロックせずに確認できますが、利用可能なデータの量は確認できません。しかし、利用可能なデータを使用して接続で読み取りを行うと、十分な大きさのバッファを与えていれば、ブロックせずにすぐに返され、利用可能な限り多くのデータが返されます。次に、このデータをどこかにバッファリングして、バッファ内のデータ量を利用できるようにすることを選択できますが、とにかくこれは本当に必要ではなく、先に進んでデータを処理したいだけだと何かが教えてくれます。

于 2012-10-06T00:36:36.527 に答える
2

私はたくさんのドキュメント、フォーラム、そしてもちろんあなたのそれぞれの回答を読みました。ソリューションを開発してテストしました。これは最善ではありませんが、私の最良の概算です。

SelectorのselectNow()メソッドを使用します。SocketChannelが登録されている名前を指定すると、このメソッドを使用して、関連付けられたSocketに読み取り可能なバイトがあるかどうかを知ることができます。Selectorselector

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;
}

バイトを読み取らずにソケットに読み取り可能なバイトがあるかどうかを知る唯一の方法です。それを行うためのより良い方法またはこの方法で行うことの欠点を知っていますか?

編集:

このソリューションは初めて機能しますが、欠点があります。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();
}

まず、接続をリッスンしてデータを書き込むアプリケーションを実行します。次のコマンドでnetcatnc -l 9999を使用しました。次に、Java プログラムを実行します。最後に、テキスト行を書き込むことができnetcat、私の Java プログラムは完全に動作します。

これは概念実証であり、実際のプログラミング ソリューションへの道ではないことに注意してください。実際、これは非常に悪い習慣です。@EJPと@Markusに感謝します!

于 2012-10-08T14:12:28.900 に答える
1

それを行うNIOのメソッドは知りません。先に進んで読んでください。

于 2012-09-27T16:28:33.480 に答える
-3

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();  
  }
于 2012-10-03T10:22:42.807 に答える