Повнодуплексний потік і зворотний тиск у SOCKS-ретрансляторі
Проєктуйте потік ретранслятора навколо незалежних байтових потоків, часткового I/O, обмежених буферів і зворотного тиску, а не вгаданих розмірів відповіді.
Рукостискання SOCKS вдалося. Тепер ретранслятор читає від клієнта, пише до пункту призначення, чекає на повну відповідь і лише потім знову читає від клієнта. Така послідовність може заблокувати цілком коректний застосунок.
Після встановлення TCP-тунелю SOCKS не визначає повідомлення всередині нього. Пункт призначення може надіслати першим. Обидві сторони можуть надсилати незалежно. Одна сторона може надіслати короткий запис і потім чекати на відповідь іншої.
Передавайте байти, а не вгадані транзакції
Виклик приймання повертає доступні байти відповідно до прапорців і стану сокета. Він не повідомляє загальному ретранслятору, що відповідь застосунку завершена. Очікування заповнення довільно великого буфера може перешкодити передачі невеликого повідомлення, потрібного партнеру для продовження.
Використовуйте наявну модель конкурентності застосунку для обслуговування обох напрямків: цикл подій, асинхронний I/O або інший навмисно обраний дизайн. Важливо, що прогрес в одному напрямку не потребує вигаданої межі повідомлення в іншому.
Тримайте два обмежені очікувані буфери
Дані, прочитані від клієнта, можуть усе ще очікувати доставки до пункту призначення, тоді як дані з пункту призначення очікують доставки клієнту. Відстежуйте ці очікувані байти окремо.
Надсилання може записати менше байтів, ніж запитано. Зберігайте незаписану частину; не відкидайте її та не відтворюйте весь буфер заново. Тимчасово незаписуваний сокет — причина чекати на правильну подію готовності, а не крутитися безперервно.
Обмежте буфери. Якщо пункт призначення повільний, продовження читання необмежених даних клієнта просто переносить чергу в пам'ять ретранслятора. Зворотний тиск означає призупинення читання, коли відповідний очікуваний буфер заповнений, і відновлення, коли записи звільняють місце.
Завершення в одному напрямку потребує політики
TCP може закрити напрямок надсилання, поки інший напрямок ще має корисні дані. Вирішіть, як поширювати цей стан після вичерпання буферизованих байтів, і відрізняйте впорядковане закриття від помилки чи дедлайну.
Ніщо з цього не вимагає, щоб ретранслятор розшифровував або розбирав корисне навантаження застосунку. Йому потрібна коректна обробка потоку. Саме лише збільшення розміру буфера не виправить ретранслятор, який чекає не ту сторону або припускає, що кожне читання відповідає одному цілому запиту.