简体   繁体   English

在Java中快速实现端口转发

[英]Fast implementation of a port forward in Java

I have build a simple application that opens a ServerSocket, and on connection, it connects itself to another server socket on a remote machine. 我构建了一个打开ServerSocket的简单应用程序,在连接时,它将自身连接到远程计算机上的另一个服务器套接字。 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: I know I could use IPTables on Linux, but this has to work on Windows. PS:我知道我可以在Linux上使用IPTables,但这必须在Windows上运行。

PPS: If you post implementations for this simple task, I will create a benchmark to test all given implementations. PPS:如果你发布这个简单任务的实现,我将创建一个基准来测试所有给定的实现。 The solution should be fast for many small (~100bytes) packages and steady data streams. 对于许多小型(~100字节)包和稳定数据流,解决方案应该是快速的。

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

Take a look at tcpmon . 看看tcpmon Its purpose is to monitor tcp data, but it also forwards to a different host/port. 其目的是监视tcp数据,但它也转发到不同的主机/端口。

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): 这里有一些书中获取的端口转发代码 (它不是英文的,所以我粘贴代码而不是提供书籍电子版的链接):

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. 不需要调用inputStream.available() 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. 对Socket流的read将返回当前可用的字符数,但在缓冲区已满之前不会阻塞。 (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.) (我在javadocs中找不到任何说明这一点的内容,但我确定情况确实如此。很多事情都会表现不佳......或者中断...如果read被阻止,直到缓冲区已满。)

  • As @user479257 points out, you should get better throughput by using java.nio and reading and writing ByteBuffers. 正如@ user479257指出的那样,你应该通过使用java.nio和读写ByteBuffers来获得更好的吞吐量。 This will cut down on the amount of data copying that occurs in the JVM. 这将减少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. 您应该使用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 */ }
    }
}

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 : 在同一主题上:

Did 2 reads and one buffer check per loop iteration really sped things up and have you measured that? 每次循环迭代中有2次读取和一次缓冲检查确实加快了速度并且你测量了吗? 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: 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); } 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); } 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); }这是与在第一版本中使用InputStream.read()我的一个老代理,然后去可用()检查+ 1024字节的缓冲区在第二和第三上面的代码解决。

If you really really need performance (or just want to learn), use java.nio or one of the libraries that build on it. 如果你真的需要性能(或者只是想学习),请使用java.nio或其中的一个库。 Do note that IO performance tends to behave wildly different on different platforms. 请注意,IO性能在不同平台上的行为往往会有很大差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM