用 Java 快速实现端口转发
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3954454/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Fast implementation of a port forward in Java
提问by Daniel
I have build a simple application that opens a ServerSocket, and on connection, it connects itself to another server socket on a remote machine. To implement port forwarding, I use two threads, one that reads from the local inputstream and streams to the remote sockets outputstream, and vice versa.
我已经构建了一个打开 ServerSocket 的简单应用程序,并且在连接时,它将自身连接到远程机器上的另一个服务器套接字。为了实现端口转发,我使用了两个线程,一个从本地输入流读取,然后将流传输到远程套接字输出流,反之亦然。
The implementation feels a bit inperformant, and so I ask you if you know a better implementation strategy, or even have some code lying around to achive this in a performant way.
实现感觉有点低效,所以我问你是否知道更好的实现策略,或者甚至有一些代码可以以高性能的方式实现这一点。
PS: I know I could use IPTables on Linux, but this has to work on Windows.
PS:我知道我可以在 Linux 上使用 IPTables,但这必须在 Windows 上运行。
PPS: If you post implementations for this simple task, I will create a benchmark to test all given implementations. The solution should be fast for many small (~100bytes) packages and steady data streams.
PPS:如果你发布这个简单任务的实现,我将创建一个基准来测试所有给定的实现。对于许多小型(~100 字节)包和稳定的数据流,该解决方案应该很快。
My current implementation is this (executed on each of the two threads for each direction):
我当前的实现是这样的(在每个方向的两个线程上执行):
public static void route(InputStream inputStream, OutputStream outputStream) throws IOException {
byte[] buffer = new byte[65536];
while( true ) {
// Read one byte to block
int b = inputStream.read();
if( b == - 1 ) {
log.info("No data available anymore. Closing stream.");
inputStream.close();
outputStream.close();
return;
}
buffer[0] = (byte)b;
// Read remaining available bytes
b = inputStream.read(buffer, 1, Math.min(inputStream.available(), 65535));
if( b == - 1 ) {
log.info("No data available anymore. Closing stream.");
inputStream.close();
outputStream.close();
return;
}
outputStream.write(buffer, 0, b+1);
}
}
采纳答案by Stephen C
A couple of observations:
几个观察:
The one byte read at the start of the loop does nothing to improve performance. Probably the reverse in fact.
The call to
inputStream.available()
is unnecessary. You should just try to read to "buffer size" characters. Aread
on a Socket streamwill return as many characters as are currently available, but won't block until the buffer is full. (I cannot find anything in the javadocs that says this, but I'm sure it is the case. A lot of things would perform poorly ... or break ... ifread
blocked until the buffer was full.)As @user479257 points out, you should get better throughput by using java.nio and reading and writing ByteBuffers. This will cut down on the amount of data copying that occurs in the JVM.
Your method will leak Socket Streams if a read, write or close operation throws an exception. You should use a
try ... finally
as follows to ensure that the streams are always closed no matter what happens.
在循环开始时读取的一个字节不会提高性能。事实上可能正好相反。
调用
inputStream.available()
是不必要的。您应该尝试读取“缓冲区大小”字符。read
Socket 流上的A将返回当前可用的尽可能多的字符,但在缓冲区已满之前不会阻塞。(我在 javadoc 中找不到任何说明这一点的内容,但我确信确实如此。如果read
在缓冲区已满之前被阻塞,很多事情都会表现不佳......或中断......。)正如@user479257 指出的那样,您应该通过使用 java.nio 和读写 ByteBuffers 来获得更好的吞吐量。这将减少 JVM 中发生的数据复制量。
如果读取、写入或关闭操作引发异常,您的方法将泄漏 Socket Streams。您应该
try ... finally
按如下方式使用 a以确保无论发生什么情况都始终关闭流。
public static void route(InputStream inputStream, OutputStream outputStream)
throws IOException {
byte[] buffer = new byte[65536];
try {
while( true ) {
...
b = inputStream.read(...);
if( b == - 1 ) {
log.info("No data available anymore. Closing stream.");
return;
}
outputStream.write(buffer, 0, b+1);
}
} finally {
try { inputStream.close();} catch (IOException ex) { /* ignore */ }
try { outputStream.close();} catch (IOException ex) { /* ignore */ }
}
}
回答by Bozho
Take a look at tcpmon. Its purpose is to monitor tcp data, but it also forwards to a different host/port.
看看tcpmon。它的目的是监控 tcp 数据,但它也转发到不同的主机/端口。
And here is some codefor port forwarding taken from a book(it's not in English, so I'm pasting the code rather than giving a link to the book e-version):
回答by Colin Hebert
If your code isn't performant, maybe your buffers aren't large enough.
如果您的代码性能不佳,则可能是您的缓冲区不够大。
Too small buffers mean that more request will be done and less performances.
缓冲区太小意味着将完成更多的请求而性能降低。
On the same topic :
在同一主题上:
回答by bod
Did 2 reads and one buffer check per loop iteration really sped things up and have you measured that? Looks like a premature optimization to me... From personal experience, simply reading into a small buffer and then writing it to the output works well enough. Like that:byte[] buf = new byte[1024];
int read = m_is.read(buf);
while(read != -1)
{
m_os.write(buf, 0, read);
m_fileOut.write(buf, 0, read);
read = m_is.read(buf);
}
This is from an old proxy of mine that used InputStream.read() in the first version, then went to available() check + 1024 byte buffer in the second and settled on the code above in the third.
每次循环迭代进行 2 次读取和一次缓冲区检查是否真的加快了速度,您是否对此进行了测量?对我来说似乎是一个过早的优化......根据个人经验,只需读入一个小缓冲区然后将其写入输出就足够了。像这样:byte[] buf = new byte[1024];
int read = m_is.read(buf);
while(read != -1)
{
m_os.write(buf, 0, read);
m_fileOut.write(buf, 0, read);
read = m_is.read(buf);
}
这是我的一个旧代理,它在第一个版本中使用 InputStream.read(),然后在第二个版本中使用 available() 检查 + 1024 字节缓冲区,并在第三个版本中解决了上面的代码。
If you really really need performance (or just want to learn), use java.nio or one of the libraries that build on it. Do note that IO performance tends to behave wildly different on different platforms.
如果您真的需要性能(或只是想学习),请使用 java.nio 或基于它的库之一。请注意,IO 性能在不同平台上的表现往往大不相同。