Fast port implementation in Java

I have a simple application that opens a ServerSocket, and when connected, it connects to another server socket on the remote machine. To implement port forwarding, I use two streams: one that reads from the local input stream and the streams to the output stream of remote sockets and vice versa.

The implementation seems a little inefficient, and so I ask you if you know about a better implementation strategy or even use some kind of code to achieve this in your own way.

PS: I know that I can use IPTables on Linux, but this should work on Windows.

PPS: If you publish implementations for this simple task, I will create a control test to test all the given implementations. The solution should be fast for many small packets (~ 100 bytes) and robust data streams.

My current implementation is this (performed for each of 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); } } 
+6
java portforwarding
source share
4 answers

A few observations:

  • A single byte read at the beginning of the loop does nothing to improve performance. Probably the opposite.

  • The call to inputStream.available() not needed. You should simply try to read the characters "buffer size". A read in the Socket stream will return as many characters as are currently available, but will not block until the buffer is full. (I can't find anything in javadocs that says this, but I'm sure it is. Many things will work poorly ... or break ... if read blocked until the buffer is full.)

  • As @ user479257 points out, you should get higher throughput using java.nio and reading and writing ByteBuffers. This will reduce the amount of data copying that occurs in the JVM.

  • Your method will leak Socket Streams if a read, write, or close operation throws an exception. You should use try ... finally as follows to ensure that threads 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 */ } } } 
+6
source share

Take a look at tcpmon . Its purpose is to track tcp data, but also forwarded to another host / port.

And here is some code for port forwarding, taken from the book (this is not in English, so I am inserting the code, not the link to the electronic book):

+8
source share

If your code does not work, your buffers may not be large enough.

Too small buffers mean more requests will be made and less execution.


In the same topic:

  • How to determine the ideal buffer size when using FileInputStream?
0
source share

Was there 2 readings and one buffer check for each iteration of the loop really accelerated the situation and you measured it? It looks like a premature optimization for me ... From personal experience, just reading into a small buffer and then writing it to the output works quite well. For example: 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 my old proxy server that used InputStream.read () in the first version, then switched to the available () check + 1024 byte buffer in the second and set the code above in the third.

If you really need performance (or just want to learn), use java.nio or one of the libraries that build on it. Note that I / O performance tends to behave on different platforms on different platforms.

0
source share

All Articles