2009-10-15 6 views
67

обновление в java9: https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#transferTo-java.io.OutputStream-Подключение входного потока к OutputStream

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

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

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

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

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

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

ответ

81

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

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

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

+0

Да, я думаю, что это очищает вещи. Я думаю, что меня путают с readFully(), который требует заполнения буфера. – jbu

+1

Я пробовал свой код, и я также попытался прочитать сообщение по сообщениям, прочитав длину сообщения, а затем выполнив байт [] buf = length; inputstream.read (buf) .... последний способ был быстрее, и я не уверен, почему. Кажется, что он выполняет больше строк кода, но это быстрее. Почти в 2 раза быстрее. – jbu

+0

Кроме того, поскольку мой сервер имеет несколько источников и один приемник, я думаю, что мне нужно читать * сообщения * в нескольких источниках, потому что просто буферы чтения и пересылки могут чередовать сообщения между клиентами и скремблировать их. – jbu

9

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

код

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


Mod подробнее е

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

66

Как насчет использования

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

и быть сделано с ним?

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

+22

или просто использовать эту функцию, так как не требуется другая функция с теми же параметрами. .. –

+0

да, это то, что я делаю лично. Я предполагаю, что я только набрал дополнительное имя метода как документацию, но это не нужно. –

+1

Из того, что я могу сказать, этот метод блокируется до тех пор, пока входной сигнал не пройдет. Поэтому это должно быть сделано в асинхронном потоке для вопросника. – Jonah

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

Это не полезно – ThomasRS

+0

Это для передачи данных из одного потока в другой без блокировки текущей темы. –

-14

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

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 с небольшим количеством доступных памятью, потому что с рекурсивной функцией вы можете легко получить ошибку исключения переполнения стека

+1

lolz .. stackoverflow исключение ошибка !!! Я уверен, что сайт может опуститься –

+11

-1: Как рекурсивное решение Scala относится к Java-вопросу? – tomlogic

+1

Метод recurse является хвостом рекурсивным. Если вы обмениваете его с помощью @tailrec, у вас не будет проблем с переполнением стека. –

2

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

Должно быть намного меньше 10 МБ. Это TCP, о котором мы говорим. Любой размер, превышающий буфер приема сокета, совершенно бессмыслен, и они измеряются в килобайтах, а не в мегабайтах. – EJP

1

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

+0

Не могли бы вы предоставить некоторую аналогичную реализацию Java? – Luchostein

+1

@ Luchostein: Я отвечал на багги-ответ Scala Джорджем Плигором ниже – pathikrit

0

Использование org.apache.commons.io.IOUtils

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

или copyLarge для размера> 2GB

 Смежные вопросы

  • Нет связанных вопросов^_^