Подключение входного потока к выходному потоку

обновление в java9: https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#transferTo-java.io.OutputStream-

Я видел некоторые похожие, но не совсем-то-то-необходимые потоки.

У меня есть сервер, который будет в основном принимать данные от клиента, клиента A и пересылать его, байт для байта, другому клиенту, клиенту B.

Я хочу связать свой входной поток клиента A с моим потоком вывода клиента B. Возможно ли это? Каковы способы сделать это?

Кроме того, эти клиенты отправляют друг другу сообщения, которые несколько чувствительны к времени, поэтому буферизация не будет выполняться. Я не хочу, чтобы буфер сказал 500, и клиент отправляет 499 байт, а затем мой сервер удерживает при пересылке 500 байтов, потому что он не получил последний байт, чтобы заполнить буфер.

В настоящий момент я разбираю каждое сообщение, чтобы найти его длину, затем считывая байты длины, а затем пересылая их. Я решил (и испытал), что это будет лучше, чем чтение байта и пересылка байта снова и снова, потому что это будет очень медленно. Я также не хотел использовать буфер или таймер по той причине, о которой я говорил в своем последнем абзаце, - я не хочу, чтобы сообщения ожидали очень долгое время, чтобы пройти через просто потому, что буфер не заполнен.

Какой хороший способ сделать это?

Ответ 1

Просто потому, что вы используете буфер, это не означает, что поток должен заполнить этот буфер. Другими словами, это должно быть хорошо:

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 будет блокироваться до тех пор, пока не будут доступны некоторые данные, но он не будет ждать, пока он не станет доступен для заполнения буфера. (Я полагаю, что это возможно, и я полагаю, что FileInputStream обычно заполняет буфер, но поток, подключенный к сокету, скорее всего, даст вам данные немедленно.)

Я думаю, что стоит хотя бы попробовать это простое решение.

Ответ 2

Как просто использовать

void feedInputToOutput(InputStream in, OutputStream out) {
   IOUtils.copy(in, out);
}

и сделать с ним?

из библиотеки jacarta apache commons i/o, которая уже используется огромным количеством проектов, поэтому вы, вероятно, уже имеете банку в своем пути к классам.

Ответ 4

Вы можете использовать круговой буфер:

код

// 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>


Детали режима

http://ostermiller.org/utils/CircularBuffer.html

Ответ 5

Асинхронный способ его достижения.

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

Ответ 7

BUFFER_SIZE - размер патронов для чтения. Должно быть > 1kb и < 10MB.

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

Ответ 8

Это версия Scala, которая является чистой и быстрой (без stackoverflow):

  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, а также передать пользовательские буферы/размеры.

Ответ 9

Используйте org.apache.commons.io.IOUtils

InputStream inStream = new ...
OutputStream outStream = new ...
IOUtils.copy(inStream, outStream);

или copyLarge для размерa > 2 ГБ

Ответ 10

Если вы входите в функционал, это функция, написанная в Scala, показывающая, как вы можете копировать входной поток в выходной поток, используя только vals (а не vars).

def copyInputToOutputFunctional(inputStream: InputStream, outputStream: OutputStream,bufferSize: Int) {
  val buffer = new Array[Byte](bufferSize);
  def recurse() {
    val len = inputStream.read(buffer);
    if (len > 0) {
      outputStream.write(buffer.take(len));
      recurse();
    }
  }
  recurse();
}

Обратите внимание, что это не рекомендуется использовать в Java-приложении с небольшой доступной памятью, поскольку с помощью рекурсивной функции вы можете легко получить ошибку исключения