似ているが、まったく必要ではないスレッドをいくつか見ました。
私はサーバーを持っています。これは基本的に、クライアントであるクライアントAから入力を受け取り、それをバイトごとに別のクライアントであるクライアントBに転送します。
クライアントAの入力ストリームをクライアントBの出力ストリームに接続したいのですが、可能ですか?それを行う方法は何ですか?
また、これらのクライアントはお互いにメッセージを送信していますが、メッセージは多少時間に依存するため、バッファリングは行いません。たとえば500のバッファは必要ありません。クライアントは499バイトを送信しますが、バッファを満たす最後のバイトを受信していないため、サーバーは500バイトの転送を保留します。
現在、私は各メッセージを解析してその長さを見つけ、次にlengthバイトを読み取ってから転送しています。これは非常に遅いため、バイトを読み取って繰り返し転送するよりも優れていると考えました(そしてテストしました)。また、最後の段落で述べた理由でバッファーやタイマーを使用したくありませんでした。バッファーがいっぱいではないという理由だけで、メッセージが本当に長い間待機するのは望ましくありません。
これを行う良い方法は何ですか?
バッファを使用しているからといって、ストリームがfillそのバッファを必要とするわけではありません。言い換えれば、これは大丈夫です:
public static void copyStream(InputStream input, OutputStream output)
throws IOException
{
byte[] buffer = new byte[1024]; // Adjust if you want
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1)
{
output.write(buffer, 0, bytesRead);
}
}
基本的にread
呼び出しはsomeデータが利用可能になるまでブロックしますが、バッファを満たすためにallが利用可能になるまで待機しません。 (私はそれができると思うし、FileInputStream
は通常willバッファを埋めると思うが、ソケットに接続されたストリームはすぐにデータを提供する可能性が高い。)
少なくともこの単純な解決策を最初に試す価値があると思います。
使ってみてはいかがですか
void feedInputToOutput(InputStream in, OutputStream out) {
IOUtils.copy(in, out);
}
そしてそれでやる?
すでに膨大な量のプロジェクトで使用されているジャカルタのApache commons i/oライブラリから、おそらくクラスパスにすでにjarが含まれている可能性があります。
完全を期すため、 guava には 便利なユーティリティ もあります
ByteStreams.copy(input, output);
JDK 9 が追加されました InputStream#transferTo(OutputStream out)
この機能用。
循環バッファを使用できます:
コード
// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());
Maven依存関係
<dependency>
<groupId>org.ostermiller</groupId>
<artifactId>utils</artifactId>
<version>1.07.00</version>
</dependency>
モードの詳細
それを達成するための非同期の方法。
void inputStreamToOutputStream(final InputStream inputStream, final OutputStream out) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
int d;
while ((d = inputStream.read()) != -1) {
out.write(d);
}
} catch (IOException ex) {
//TODO make a callback on exception.
}
}
});
t.setDaemon(true);
t.start();
}
BUFFER_SIZEは読み込むチャックのサイズです。1kb以上10 MB未満である必要があります。
private static final int BUFFER_SIZE = 2 * 1024 * 1024;
private void copy(InputStream input, OutputStream output) throws IOException {
try {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = input.read(buffer);
while (bytesRead != -1) {
output.write(buffer, 0, bytesRead);
bytesRead = input.read(buffer);
}
//If needed, close streams.
} finally {
input.close();
output.close();
}
}
Org.Apache.commons.io.IOUtilsを使用します
InputStream inStream = new ...
OutputStream outStream = new ...
IOUtils.copy(inStream, outStream);
またはcopyLargeサイズが2GBを超える場合
これはScalaクリーンで高速なバージョンです(スタックオーバーフローなし):
import scala.annotation.tailrec
import Java.io._
implicit class InputStreamOps(in: InputStream) {
def >(out: OutputStream): Unit = pipeTo(out)
def pipeTo(out: OutputStream, bufferSize: Int = 1<<10): Unit = pipeTo(out, Array.ofDim[Byte](bufferSize))
@tailrec final def pipeTo(out: OutputStream, buffer: Array[Byte]): Unit = in.read(buffer) match {
case n if n > 0 =>
out.write(buffer, 0, n)
pipeTo(out, buffer)
case _ =>
in.close()
out.close()
}
}
これにより、>
シンボル例inputstream > outputstream
また、カスタムバッファ/サイズも渡します。