7

我已经构建了一个简单的应用程序,它打开一个 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);
    }
}
4

4 回答 4

9

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

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

于 2010-10-17T18:16:29.353 回答
7

几点观察:

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

  • 调用 toinputStream.available()是不必要的。您应该尝试阅读“缓冲区大小”字符。Socket 流上的Aread将返回当前可用的尽可能多的字符,但在缓冲区已满之前不会阻塞。(我在 javadocs 中找不到任何说明这一点的内容,但我确信确实如此。如果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 */ }
    }
}
于 2010-10-18T13:18:31.533 回答
0

如果您的代码性能不佳,则可能是您的缓冲区不够大。

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


在同一主题上:

于 2010-10-17T17:52:10.023 回答
0

每次循环迭代 2 次读取和 1 次缓冲区检查真的加快了速度吗?你测量过吗?对我来说看起来像是一个过早的优化......根据个人经验,只需读取一个小缓冲区然后将其写入输出就足够了。像这样: 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 字节缓冲区,并在第三个版本中确定上面的代码。

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

于 2010-10-18T12:57:15.480 回答