在java9中更新:https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#transferTo-java.io.OutputStream-
我看到了一些类似的但不是我需要的线程。
我有一个服务器,它基本上从客户端A客户端接收输入,并将其逐字节转发到另一个客户端,客户端B.
我想将客户端A的输入流与客户端B的输出流连接起来。这可能吗?有什么方法可以做到这一点?
此外,这些客户端正在发送彼此有时间敏感的消息,因此缓冲不会。我不想要500的缓冲区,客户端发送499个字节,然后我的服务器在转发500个字节时保持关闭,因为它没有收到填充缓冲区的最后一个字节。
现在,我正在解析每条消息以找到它的长度,然后读取长度字节,然后转发它们。我想(并测试)这比读取一个字节并反复转发一个字节更好,因为这将非常慢。我也不想使用缓冲区或计时器,因为我在上一段中说过 - 我不希望消息等待很长时间才能通过,因为缓冲区未满。
这样做的好方法是什么?
答案 0 :(得分:84)
仅仅因为您使用缓冲区并不意味着流必须填充该缓冲区。换句话说,这应该没问题:
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
通常将填充缓冲区,但附加到套接字的流更有可能立即为您提供数据。)
我认为至少首先尝试这个简单的解决方案是值得的。
答案 1 :(得分:74)
如何使用
void feedInputToOutput(InputStream in, OutputStream out) {
IOUtils.copy(in, out);
}
并完成它?
来自jakarta apache公共的i / o库已被大量项目使用,所以你可能已经在你的类路径中已经有了jar。
答案 2 :(得分:21)
答案 3 :(得分:10)
答案 4 :(得分: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>
模式详情
答案 5 :(得分: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();
}
答案 6 :(得分:2)
BUFFER_SIZE是要读入的chucks的大小。应该是&gt; 1kb和&lt; 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();
}
}
答案 7 :(得分:1)
使用org.apache.commons.io.IOUtils
InputStream inStream = new ...
OutputStream outStream = new ...
IOUtils.copy(inStream, outStream);
或 copyLarge ,尺寸为&gt; 2GB
答案 8 :(得分:0)
这是一个干净而快速的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 :(得分:-14)
如果您正在使用它,这是一个用Scala编写的函数,显示如何仅使用val(而不是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应用程序中使用,因为使用递归函数可以轻松获得堆栈溢出异常错误