在Java中快速实现端口转发 [英] Fast implementation of a port forward in Java

查看:297
本文介绍了在Java中快速实现端口转发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我构建了一个打开ServerSocket的简单应用程序,在连接时,它将自身连接到远程计算机上的另一个服务器套接字。为了实现端口转发,我使用两个线程,一个从本地输入流读取并流到远程套接字输出流,反之亦然。

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.

实现感觉有点不太合适,所以我问你是否知道更好的实施策略,或者甚至有一些代码可以以高效的方式实现这一目标。

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:我知道我可以在Linux上使用IPTables,但这必须在Windows上运行。

PS: I know I could use IPTables on Linux, but this has to work on Windows.

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

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.

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

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


推荐答案

A几个观察结果:


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

  • The one byte read at the start of the loop does nothing to improve performance. Probably the reverse in fact.

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

The call to inputStream.available() is unnecessary. You should just try to read to "buffer size" characters. A read 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 ... if read blocked until the buffer was full.)

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

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.

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

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.

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

这篇关于在Java中快速实现端口转发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆