Java中端口转发的快速实现

Java中端口转发的快速实现,java,portforwarding,Java,Portforwarding,我构建了一个简单的应用程序,它打开一个ServerSocket,在连接时,它将自己连接到远程机器上的另一个服务器套接字。为了实现端口转发,我使用了两个线程,一个线程从本地inputstream和远程套接字outputstream读取数据,另一个线程从远程套接字outputstream读取数据 这个实现感觉有点性能不佳,所以我问您是否知道更好的实现策略,或者是否有一些代码可以以一种性能良好的方式实现这一点 PS:我知道我可以在Linux上使用IPTables,但这必须在Windows上运行 PPS

我构建了一个简单的应用程序,它打开一个ServerSocket,在连接时,它将自己连接到远程机器上的另一个服务器套接字。为了实现端口转发,我使用了两个线程,一个线程从本地inputstream和远程套接字outputstream读取数据,另一个线程从远程套接字outputstream读取数据

这个实现感觉有点性能不佳,所以我问您是否知道更好的实现策略,或者是否有一些代码可以以一种性能良好的方式实现这一点

PS:我知道我可以在Linux上使用IPTables,但这必须在Windows上运行

PPS:如果您发布这个简单任务的实现,我将创建一个基准测试来测试所有给定的实现。对于许多小(约100字节)包和稳定的数据流,解决方案应该是快速的

我当前的实现是这样的(在每个方向的两个线程上执行):


如果代码性能不好,可能是缓冲区不够大

缓冲区太小意味着将执行更多的请求,而性能会降低


关于同一主题:
看一看。它的目的是监视tcp数据,但也会转发到不同的主机/端口


对于从a获取的端口转发(它不是英文的,所以我粘贴代码,而不是给书的电子版提供链接):

每个循环迭代2次读取和一次缓冲区检查真的加快了速度吗?你测量过吗?在我看来这是一个过早的优化。。。根据个人经验,只需将数据读入一个小缓冲区,然后将其写入输出即可。像这样:

字节[]buf=新字节[1024];
int read=m_为读取(buf);
while(读取!=-1)
{
m_os.write(buf,0,read);
m_fileOut.write(buf,0,read);
read=m_为.read(buf);
}
这来自我的一个旧代理,它在第一个版本中使用了InputStream.read(),然后在第二个版本中转到可用()检查+1024字节缓冲区,并在第三个版本中解决了上面的代码


如果您真的需要性能(或只是想学习),请使用java.nio或基于它构建的库之一。请注意,IO性能在不同平台上的表现往往大不相同。

有几点观察:

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

  • 不需要调用
    inputStream.available()
    。您应该尝试读取“缓冲区大小”字符。套接字流上的
    读取
    将返回当前可用的字符数,但在缓冲区已满之前不会阻塞。(我在javadocs中找不到任何这样的内容,但我确信确实如此。如果
    read
    在缓冲区满之前被阻塞,很多事情都会执行得很差……或者中断。)

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

  • 如果读、写或关闭操作引发异常,您的方法将泄漏套接字流。你应该使用
    试试。。。最后
    如下所示,以确保无论发生什么情况,流始终关闭



与其说是一种“感觉”,不如说你能衡量并详细说明这种实现的错误之处——延迟吗?吞吐量系统负载?我连接到一个SSH服务器,我的实现非常落后。但我已经发现(通过使用一些重定向端口),我有5次相同的流量通过网络传输,而不是我所比较的1次。自拍。然而,问题仍然存在:我可以让上面的过程更快吗?我使用JPortForward实现这一点,它与您的实现方式几乎相同。除非你有很重的负载,否则我无法想象你能对性能产生多大的影响,但如果你需要更快,你可能应该看看单独的Windows和Linux本机套接字API,以及异步回调等。我想这将是一个很大的工作,但收获不大。另一个类似的问题:愚蠢的我。。。我以为read(byte[])会填充字节数组,但firstLink似乎已损坏。在google中找到的代码或+1的Github链接我在开始时已经找到了不必要的字节。但是你错过了最后的b+1,应该是b。读NIO ByteBuffers快吗?如果它们不是来自磁盘而是来自网络接口,那么它们不就是普通的字节数组吗?@Daniel-我的工作不是查找所有的bug。:-)
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);
    }
}
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 */ }
    }
}