Быстрое внедрение порта вперед в Java - PullRequest
7 голосов
/ 17 октября 2010

Я создал простое приложение, которое открывает ServerSocket и при подключении подключается к другому сокету сервера на удаленной машине. Для реализации переадресации портов я использую два потока, один из которых читает локальный входной поток и передает его в выходной поток удаленных сокетов, и наоборот.

Реализация выглядит немного неэффективной, и поэтому я спрашиваю вас, знаете ли вы лучшую стратегию реализации, или даже у вас есть какой-то код, чтобы добиться этого эффективным способом.

PS: я знаю, что могу использовать IPTables в Linux, но это должно работать в 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 ]

9 голосов
/ 17 октября 2010

Взгляните на tcpmon . Его целью является мониторинг данных tcp, но он также перенаправляет на другой хост / порт.

И вот код для переадресации портов, взятый из книги (это не на английском языке, поэтому я вставляю код, а не даю ссылку на книгу. версия):

7 голосов
/ 18 октября 2010

Пара наблюдений:

  • Однобайтовое чтение в начале цикла не влияет на производительность. Вероятно, наоборот.

  • Звонок на inputStream.available() не нужен. Вы должны просто попытаться прочитать символы «размера буфера». read в потоке Socket вернет столько символов, сколько доступно в данный момент, но не будет блокироваться, пока буфер не заполнится. (Я не могу найти в javadocs ничего такого, что говорит об этом, но я уверен, что это так. Многие вещи будут работать плохо ... или сломаться ... если read заблокирован, пока буфер не будет заполнен.) 1012 *

  • Как указывает @ user479257, вы должны повысить пропускную способность, используя java.nio и читая и записывая ByteBuffers. Это сократит объем копирования данных, который происходит в JVM.

  • Ваш метод будет пропускать Socket Streams, если операция чтения, записи или закрытия выдает исключение. Вы должны использовать 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 */ }
    }
}
0 голосов
/ 18 октября 2010

Действительно ли 2 чтения и одна проверка буфера за итерацию цикла ускорили процесс, и вы это измерили? Для меня это выглядит преждевременной оптимизацией ... Исходя из личного опыта, просто чтение в небольшой буфер и запись его в выходной файл работает достаточно хорошо. Например: 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 байт во второй версии и остановился на приведенном выше коде в третьей.

Если вам действительно нужна производительность (или вы просто хотите учиться), используйте java.nio или одну из библиотек, которые на ней основываются. Обратите внимание, что производительность ввода-вывода на разных платформах может сильно отличаться.

0 голосов
/ 17 октября 2010

Если ваш код не является производительным, возможно, ваши буферы недостаточно велики.

Слишком маленькие буферы означают, что будет выполняться больше запросов и меньше производительности.


Вкл.та же тема:

...