Java中端口转发的快速实现
我构建了一个打开 ServerSocket 的简单应用程序,并在连接时将自身连接到远程计算机上的另一个服务器套接字。为了实现端口转发,我使用两个线程,一个线程从本地输入流读取数据并流到远程套接字输出流,反之亦然。
实现感觉有点低效,所以我问您是否知道更好的实现策略,或者甚至有一些代码可以以高性能的方式实现这一点。
PS:我知道我可以在 Linux 上使用 IPTables,但这必须在 Windows 上运行。
PPS:如果您发布这个简单任务的实现,我将创建一个基准来测试所有给定的实现。该解决方案对于许多小(约 100 字节)包和稳定的数据流来说应该是快速的。
我当前的实现是这样的(在每个方向的两个线程中执行):
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
查看 tcpmon。它的目的是监视 tcp 数据,但它也转发到不同的主机/端口。
这里有一些代码,用于从 book (它不是英文的,所以我粘贴代码而不是提供书籍电子版的链接):
Take a look at tcpmon. Its purpose is to monitor tcp data, but it also forwards to a different host/port.
And here is some code for 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):
一些观察结果:
在循环开始时读取一个字节对于提高性能没有任何作用。事实上可能恰恰相反。
不需要调用
inputStream.available()
。您应该尝试读取“缓冲区大小”字符。 Socket 流上的读取将返回当前可用的尽可能多的字符,但在缓冲区已满之前不会阻塞。 (我在 javadoc 中找不到任何说明这一点的内容,但我确信情况确实如此。如果read
阻塞直到缓冲区,很多事情都会表现不佳......或中断......已满。)正如 @user479257 指出的那样,您应该通过使用 java.nio 以及读写 ByteBuffers 来获得更好的吞吐量。这将减少 JVM 中发生的数据复制量。
如果读取、写入或关闭操作抛出异常,您的方法将泄漏套接字流。您应该使用
try ...finally
,如下所示,以确保无论发生什么情况,流始终关闭。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.如果您的代码性能不佳,则可能是您的缓冲区不够大。
缓冲区太小意味着将完成更多请求,但性能会降低。
关于同一主题:
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 :
每个循环迭代进行 2 次读取和一次缓冲区检查是否确实加快了速度,您是否对此进行了测量?对我来说,这似乎是一个过早的优化......从个人经验来看,简单地读入一个小缓冲区,然后将其写入输出就足够了。像这样:<代码>
字节[] buf = 新字节[1024];
int read = m_is.read(buf);
而(读!= -1)
{
m_os.write(buf, 0, 读);
m_fileOut.write(buf, 0, 读);
读取 = m_is.read(buf);
}
这是我的一个旧代理,在第一个版本中使用了 InputStream.read() ,然后在第二个版本中使用了 available() 检查 + 1024 字节缓冲区,并在第三个版本中使用了上面的代码。
如果您确实需要性能(或者只是想学习),请使用 java.nio 或基于其构建的库之一。请注意,IO 性能在不同平台上的表现往往有很大差异。
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:
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.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);
}
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.