18

NIOライブラリで遊んでいます。ポート8888で接続をリッスンしようとしています。接続が受け入れられたら、そのチャネルからにすべてをダンプしますsomefile

でそれを行う方法は知っていますがByteBuffers、非常に効率的だと言われているで動作させたいと思いますFileChannel.transferFrom

これは私が得たものです:

ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.socket().bind(new InetSocketAddress(8888));

SocketChannel sChannel = ssChannel.accept();
FileChannel out = new FileOutputStream("somefile").getChannel();

while (... sChannel has not reached the end of the stream ...)     <-- what to put here?
    out.transferFrom(sChannel, out.position(), BUF_SIZE);

out.close();

だから、私の質問は、「transferFromストリームの終わりに達するまで、いくつかのチャネル」をどのように表現するのですか?


編集:使用されるバッファのサイズは質問とは無関係であるため、1024をBUF_SIZEに変更しました。

4

7 に答える 7

13

ケースを処理する方法はいくつかあります。trasnferTo/From が内部でどのように実装されているか、いつ優れているかについての背景情報。

  • 何よりもまず、xfer する必要があるバイト数を知っておく必要があります。つまり、使用FileChannel.size()可能な最大値を決定し、結果を合計するために使用します。ケースは、FileChannel.trasnferTo(socketChanel)
  • メソッドが -1 を返さない
  • メソッドは Windows でエミュレートされます。Windows には、ファイル記述子からソケットに xfer する API 関数がありません。名前で指定されたファイルから xfer する API 関数が 1 つ (2 つ) ありますが、これは Java API と互換性がありません。
  • Linux では標準のsendfile (または sendfile64) が使用され、Solaris ではsendfilev64.

要するにfor (long xferBytes=0; startPos + xferBytes<fchannel.size();) doXfer()、ファイルからの転送で機能します->ソケット。ソケットからファイルに転送するOS機能はありません(OPが関心を持っています)。ソケット データは OS キャッシュ内にないため、効果的に行うことができないため、エミュレートされます。コピーを実装する最良の方法は、ソケット読み取りバッファーのサイズのポーリングされた直接 ByteBuffer を使用する標準ループを介することです。セレクターも含む非ブロッキング IO のみを使用するためです。

つまり、超効率的と言われている "? で動作させたいのですが、効率的ではなく、すべての OS でエミュレートされているため、ソケットが適切に閉じられたかどうかにかかわらず、転送が終了します。関数は転送があった場合 (ソケットが読み取り可能で開いている場合)、継承された IOException をスローすることさえありません。

答えが明確であることを願っています。 の唯一の興味深い使用法はFile.transferFrom、ソースがファイルの場合です。最も効率的な (そして興味深いケース) は file->socket であり、file->file はfilechanel.map/ unmap(!!)を介して実装されます。

于 2012-05-08T06:00:14.613 に答える
4

よくわかりませんが、JavaDoc には次のように書かれています。

ソースチャネルから最大 count バイトを読み取り、指定された位置からこのチャネルのファイルに書き込む試みが行われます。このメソッドを呼び出すと、要求されたすべてのバイトが転送される場合と転送されない場合があります。そうするかどうかは、チャネルの性質と状態に依存します。ソース チャネルの残りのバイト数が count バイトより少ない場合、またはソース チャネルが非ブロッキングであり、入力バッファーですぐに使用できるバイト数が count バイトより少ない場合は、要求されたバイト数よりも少ないバイト数が転送されます。

無限バイトをコピーするように指示すると(もちろんループではありません)、仕事ができると言うかもしれません:

out.transferFrom(sChannel, out.position(), Integer.MAX_VALUE);

transferFromしたがって、ソケット接続が閉じられると、状態が変更され、メソッドが停止すると思います。

しかし、すでに述べたように、よくわかりません。

于 2011-10-04T17:23:03.340 に答える
4

あなたの質問に直接答える:

while( (count = socketChannel.read(this.readBuffer) )  >= 0) {
   /// do something
}

しかし、これを行う場合、実際にはブロッキング IO とまったく同じように使用するため、非ブロッキング IO の利点を使用しません。ノンブロッキング IO のポイントは、1 つのネットワーク スレッドが同時に複数のクライアントにサービスを提供できることです。1 つのチャネル (つまりcount == 0) から読み取るものがなければ、他のチャネル (他のクライアント接続に属する) に切り替えることができます。

したがって、ループは、終了するまで 1 つのチャネルから読み取るのではなく、実際には異なるチャネルを反復する必要があります。

このチュートリアルをご覧ください: http://rox-xmlrpc.sourceforge.net/niotut/この 問題を理解するのに役立つと思います。

于 2011-10-04T17:25:28.877 に答える
1

こちらです:

URLConnection connection = new URL("target").openConnection();
File file = new File(connection.getURL().getPath().substring(1));
FileChannel download = new FileOutputStream(file).getChannel();

while(download.transferFrom(Channels.newChannel(connection.getInputStream()),
        file.length(), 1024) > 0) {
    //Some calculs to get current speed ;)
}
于 2015-06-06T23:53:30.040 に答える
1

伝えられるところによると、非常に効率的な FileChannel.transferFrom です。

DMA アクセスとノンブロッキング IO の両方の利点が必要な場合、最善の方法は、ファイルをメモリ マップし、ソケットからメモリ マップ バッファに読み込むことです。

ただし、それにはファイルを事前に割り当てる必要があります。

于 2012-05-06T23:02:04.427 に答える
1

transferFrom()カウントを返します。ゼロを返すまで、位置/オフセットを進めながら呼び出し続けます。ただし、1024 よりもはるかに大きな数 (1 メガバイトまたは 2 メガバイト程度) から始めてください。そうしないと、この方法のメリットはあまり得られません。

編集以下のすべてのコメントに対処するために、ドキュメントには、「ソースチャネルの残りのバイト数がカウントより少ない場合、またはソースチャネルが非ブロッキングであり、すぐにカウントバイトより少ない場合、要求されたバイト数よりも少ないバイトが転送される」と記載されています入力バッファで利用可能です。」したがって、ブロッキング モードの場合、ソースに何も残らない限り、ゼロは返されません。したがって、ゼロを返すまでループすることは有効です。

編集2

転送方法は確かに設計ミスです。read()すべてのメソッドと同様に、ストリームの最後に -1 を返すように設計されている必要があります。

于 2011-10-05T02:47:09.127 に答える