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

几点意见:

  • 在循环开始时读取的一个字节对提高性能没有任何作用。 事实上可能相反。

  • 不需要调用inputStream.available() 。 您应该尝试读取“缓冲区大小”字符。 对Socket流的read将返回当前可用的字符数,但在缓冲区已满之前不会阻塞。 (我在javadocs中找不到任何说明这一点的内容,但我确定情况确实如此。很多事情都会表现不佳……或者中断…如果read被阻止,直到缓冲区已满。)

  • 正如@ user479257指出的那样,你应该通过使用java.nio和读写ByteBuffers来获得更好的吞吐量。 这将减少JVM中发生的数据复制量。

  • 如果读取,写入或关闭操作引发exception,则您的方法将泄漏套接字流。 您应该使用try ... finally如下,以确保无论发生什么情况都始终关闭流。


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

看看tcpmon 。 它的目的是监视tcp数据,但它也转发到不同的主机/端口。

这里有一些从书中获取端口转发的代码 (它不是英文的,所以我粘贴代码而不是给出书籍电子版的链接):

如果您的代码不具备性能,那么您的缓冲区可能不够大。

缓冲区太小意味着将要完成更多请求并降低性能。


在同一主题上:

  • 使用FileInputStream时如何确定理想的缓冲区大小?

每次循环迭代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); } 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()check + 1024字节缓冲区,并在第三个代码中找到了上面的代码。

如果你真的需要性能(或者只是想学习),请使用java.nio或其中的一个库。 请注意,IO性能在不同平台上的行为往往会有很大差异。