TCP и UDP туннелирование: как это работает

Большинство туннельных инструментов работают только с HTTP. А если вам нужно открыть доступ к PostgreSQL, игровому серверу или DNS-резолверу? Тут на сцену выходят TCP- и UDP-туннели. Если вы только знакомитесь с темой, начните со статьи «Что такое туннелирование».

Ниже мы разберём, как устроено туннелирование TCP и UDP изнутри: мультиплексирование, жизненный цикл пакетов, NAT traversal, фрейминг данных и реальные сценарии с fxTunnel.

TCP vs UDP: ключевые различия

Зачем вообще разбираться в протоколах, если мы просто пробрасываем трафик? Потому что TCP и UDP устроены принципиально по-разному, и туннель должен это учитывать.

ХарактеристикаTCPUDP
СоединениеУстанавливается (3-way handshake)Без соединения
Гарантия доставкиДа (подтверждения, повторная передача)Нет
Порядок пакетовГарантированНе гарантирован
Контроль потокаДа (скользящее окно)Нет
Заголовок20–60 байт8 байт
ЗадержкаВыше (handshake + ACK)Ниже (без установления соединения)
Типичное применениеБазы данных, SSH, HTTP, RedisИгры, VoIP, DNS, IoT, видеостриминг
Поведение при потере пакетаПовторная передачаПакет утерян

Если коротко: TCP даёт надёжность ценой задержки, UDP жертвует надёжностью ради скорости. И при туннелировании каждый требует своего подхода.

Как работает TCP-туннелирование

TCP-туннель создаёт виртуальный канал между публичным портом на сервере и локальным портом на вашей машине. Все входящие TCP-соединения мультиплексируются через одно управляющее TLS-соединение между клиентом и сервером. Протокол TCP сам обеспечивает доставку и порядок, поэтому туннелю достаточно просто гонять байтовые потоки.

Жизненный цикл TCP-соединения через туннель

  1. Регистрация — клиент fxTunnel подключается к серверу и регистрирует TCP-туннель на указанный локальный порт.
  2. Выделение порта — сервер выделяет публичный порт (например, tunnel.fxtun.dev:41234).
  3. Входящее соединение — внешний клиент подключается к публичному порту, проходит TCP handshake с сервером.
  4. Мультиплексирование — сервер создаёт новый логический поток в мультиплексоре и передаёт данные клиенту fxTunnel.
  5. Локальное соединение — клиент fxTunnel устанавливает TCP-соединение с локальным сервисом и проксирует данные в обоих направлениях.
  6. Завершение — при закрытии любой из сторон соединение корректно завершается по всей цепочке.

Фрейминг и мультиплексирование TCP-потоков

Мультиплексирование — ключевой механизм TCP-туннелирования. Через одно физическое TLS-соединение между клиентом и сервером проходят десятки и сотни независимых TCP-потоков. Каждый поток имеет уникальный идентификатор, а данные разбиваются на фреймы с заголовком:

┌──────────────────────────────────────┐
│  Frame Header                        │
│  ┌──────────┬────────┬─────────────┐ │
│  │ Stream ID│ Length │ Flags       │ │
│  │ (4 bytes)│(4 bytes)│ (1 byte)  │ │
│  └──────────┴────────┴─────────────┘ │
│  ┌──────────────────────────────────┐ │
│  │  Payload (up to 64 KB)          │ │
│  └──────────────────────────────────┘ │
└──────────────────────────────────────┘

Такой подход позволяет избежать создания отдельного TLS-соединения на каждый TCP-поток, что снижает overhead и ускоряет установку соединений. Подробнее об архитектуре мультиплексирования fxTunnel.

Схема TCP-туннелирования

Схема TCP-туннелирования
Внешний клиент (psql, redis-cli, ssh)
        │
        │ TCP-соединение
        ▼
┌──────────────────────────────┐
│  fxTunnel Server             │
│  Публичный порт: 41234      │
│                              │
│  ┌────────────────────────┐  │
│  │  Мультиплексор         │  │
│  │  Stream #1 ─┐          │  │
│  │  Stream #2 ─┼─► TLS   │  │
│  │  Stream #N ─┘  conn   │  │
│  └────────────────────────┘  │
└──────────────┬───────────────┘
               │ одно TLS-соединение
               │ (все потоки мультиплексированы)
               ▼
┌──────────────────────────────┐
│  fxTunnel Client             │
│                              │
│  ┌────────────────────────┐  │
│  │  Демультиплексор       │  │
│  │  Stream #1 → :5432     │  │
│  │  Stream #2 → :5432     │  │
│  │  Stream #N → :5432     │  │
│  └────────────────────────┘  │
└──────────────────────────────┘
               │
               ▼
        localhost:5432
        (PostgreSQL)

Каждый логический поток (Stream) соответствует одному TCP-соединению от внешнего клиента. Мультиплексор упаковывает данные всех потоков в единый TLS-канал, а демультиплексор на стороне клиента распаковывает их и направляет в локальный сервис.

Как работает UDP-туннелирование

С UDP всё заметно сложнее. Туннель должен сам отслеживать виртуальные сессии, инкапсулировать датаграммы внутри надёжного TCP-соединения и корректно обрабатывать NAT traversal. Именно поэтому большинство туннельных инструментов UDP просто не поддерживают.

Основные вызовы UDP-туннелирования

  • Отсутствие состояния соединения — UDP не имеет handshake и не отслеживает соединения. Туннель должен создавать виртуальные сессии на основе пар адрес:порт отправителя.
  • NAT traversal — без установленного соединения NAT-таблицы не гарантируют стабильную маршрутизацию. Туннель решает эту проблему, инкапсулируя датаграммы внутри TCP.
  • Порядок и потеря пакетов — UDP не гарантирует ни порядок, ни доставку. Инкапсуляция в TCP добавляет гарантии на участке клиент-сервер, но конечный получатель по-прежнему работает с «сырым» UDP.
  • Размер датаграмм — UDP-пакеты имеют фиксированные границы, которые нужно сохранить при инкапсуляции. Потоковый TCP не имеет понятия «пакет», поэтому требуется фрейминг.

Инкапсуляция UDP в TCP

fxTunnel решает проблему туннелирования UDP путём инкапсуляции датаграмм внутри фреймов TCP-соединения. Каждая UDP-датаграмма оборачивается в фрейм с метаданными: адрес отправителя, длина и идентификатор виртуальной сессии.

UDP-датаграмма от игрока
        │
        ▼
┌──────────────────────────────┐
│  fxTunnel Server             │
│  Публичный UDP-порт: 52001  │
│                              │
│  ┌────────────────────────┐  │
│  │  Виртуальные сессии    │  │
│  │  Addr A:1234 → Sess #1│  │
│  │  Addr B:5678 → Sess #2│  │
│  └────────────┬───────────┘  │
│               │              │
│  ┌────────────▼───────────┐  │
│  │  Инкапсуляция          │  │
│  │  ┌──────┬──────┬─────┐ │  │
│  │  │SessID│ Len  │ Data│ │  │
│  │  └──────┴──────┴─────┘ │  │
│  └────────────┬───────────┘  │
└───────────────┼──────────────┘
                │ TLS-соединение (TCP)
                ▼
┌──────────────────────────────┐
│  fxTunnel Client             │
│                              │
│  ┌────────────────────────┐  │
│  │  Декапсуляция          │  │
│  │  Sess #1 → UDP :27015 │  │
│  │  Sess #2 → UDP :27015 │  │
│  └────────────────────────┘  │
└──────────────────────────────┘
                │
                ▼
        localhost:27015
        (игровой сервер)

На стороне сервера входящие UDP-датаграммы группируются по виртуальным сессиям (на основе адреса и порта отправителя), инкапсулируются во фреймы и передаются клиенту через TLS-соединение. Клиент декапсулирует фреймы и отправляет «сырые» UDP-датаграммы на локальный порт. Ответные датаграммы идут обратным путём.

Управление виртуальными сессиями

Поскольку UDP не имеет понятия «соединение», fxTunnel создаёт виртуальные сессии с таймаутом неактивности. Если от конкретного отправителя не поступает датаграмм в течение заданного времени (по умолчанию 60 секунд), сессия удаляется и ресурсы освобождаются. Это предотвращает утечку памяти при большом количестве кратковременных UDP-клиентов.

Сценарии использования TCP-туннелей

Нужно открыть доступ к сервису, который работает по TCP? Вот самые частые ситуации.

Базы данных (PostgreSQL, MySQL)

# Пробрасываем PostgreSQL
fxtunnel tcp 5432
# → tunnel.fxtun.dev:41234

# Подключаемся с удалённой машины
psql -h tunnel.fxtun.dev -p 41234 -U myuser mydb

Это позволяет дать коллеге или CI-серверу доступ к локальной базе данных без деплоя и конфигурации сети.

SSH-доступ

# Пробрасываем SSH
fxtunnel tcp 22
# → tunnel.fxtun.dev:43210

# Подключаемся к машине за NAT
ssh -p 43210 user@tunnel.fxtun.dev

TCP-туннель для SSH позволяет получить доступ к машине за NAT без статического IP и проброса портов. Идеально для Raspberry Pi и домашних серверов.

Redis и кэш-серверы

# Пробрасываем Redis
fxtunnel tcp 6379
# → tunnel.fxtun.dev:44567

# Подключаемся с любой машины
redis-cli -h tunnel.fxtun.dev -p 44567

Сценарии использования UDP-туннелей

В приложениях реального времени главное — минимальная задержка, а не гарантия доставки каждого пакета. Здесь UDP-туннели незаменимы.

Игровые серверы

# Пробрасываем игровой сервер (например, Counter-Strike, Minecraft)
fxtunnel udp 27015
# → tunnel.fxtun.dev:52001

# Игроки подключаются через публичный адрес

Игровые серверы работают по UDP, потому что потеря одного пакета с координатами игрока менее критична, чем задержка при повторной передаче.

DNS-сервер

# Пробрасываем локальный DNS
fxtunnel udp 53
# → tunnel.fxtun.dev:53001

# Тестируем DNS-запросы
dig @tunnel.fxtun.dev -p 53001 example.com

VoIP и IoT

VoIP (SIP, RTP), потоковое видео и IoT-устройства работают по UDP, потому что задержка критичнее потери отдельных пакетов. Через туннель можно пробросить любой UDP-порт наружу без возни с NAT.

Как fxTunnel обрабатывает оба протокола

fxTunnel мультиплексирует TCP-потоки и инкапсулированные UDP-датаграммы через единое управляющее TLS-соединение. Это ключевое архитектурное решение: один клиент может одновременно держать HTTP, TCP и UDP туннели, и все они используют один зашифрованный канал до сервера. Подробнее об архитектуре fxTunnel.

┌─────────────────────────────────────────────┐
│  Единое TLS-соединение клиент ↔ сервер      │
│                                             │
│  ┌─────────────┐ ┌───────────┐ ┌─────────┐ │
│  │ HTTP streams│ │TCP streams│ │UDP frames│ │
│  │ (мультиплекс)│(мультиплекс)│(инкапсул.)│ │
│  └─────────────┘ └───────────┘ └─────────┘ │
│                                             │
│  Control plane: регистрация, heartbeat,     │
│  управление туннелями                       │
└─────────────────────────────────────────────┘

Преимущества единого соединения:

  • Одна точка аутентификации — токен проверяется один раз при подключении.
  • Минимальный overhead — не нужно устанавливать отдельное TLS-соединение для каждого туннеля.
  • Простота для файрвола — весь трафик идёт через один исходящий порт.
  • Быстрое переподключение — при разрыве восстанавливается одно соединение, и все туннели возобновляют работу.

Подробное сравнение с ngrok и Cloudflare Tunnel — в отдельной статье. Если коротко: fxTunnel — один из немногих инструментов, который поддерживает UDP наряду с HTTP и TCP.

Практические примеры с fxTunnel

Установка

# Установка fxTunnel (Linux/macOS)
curl -fsSL https://fxtun.dev/install.sh | bash

TCP-туннели

# PostgreSQL — доступ к локальной БД
fxtunnel tcp 5432

# Redis — доступ к локальному кэшу
fxtunnel tcp 6379

# SSH — удалённый доступ к машине за NAT
fxtunnel tcp 22

UDP-туннели

# Игровой сервер — игроки подключаются через публичный адрес
fxtunnel udp 27015

# DNS — тестирование локального DNS-сервера
fxtunnel udp 53

После запуска fxTunnel выведет публичный адрес:

fxTunnel v1.x — tunnel is active
Protocol:    TCP
Public addr: tunnel.fxtun.dev:41234
Forwarding:  tunnel.fxtun.dev:41234 → localhost:5432

Press Ctrl+C to stop

Подробнее о том, как открыть localhost для доступа из интернета.

FAQ

В чём разница между TCP и UDP туннелем?

TCP-туннель передаёт потоковые соединения с гарантированной доставкой и порядком пакетов — то, что нужно для баз данных, SSH и Redis. UDP-туннель работает с датаграммами без установления соединения — подходит для игр, VoIP и DNS, где скорость важнее полноты доставки.

Поддерживает ли fxTunnel одновременно TCP и UDP?

Да, через один и тот же CLI доступны HTTP, TCP и UDP туннели. У большинства конкурентов — только HTTP и TCP (ngrok) или вообще один HTTP в бесплатном тарифе (Cloudflare Tunnel).

Почему UDP-туннелирование сложнее TCP?

Потому что у UDP нет понятия соединения. Туннелю приходится самостоятельно придумывать виртуальные сессии (по парам адрес:порт), оборачивать каждую датаграмму во фрейм на TCP-линке до сервера и решать вопросы NAT traversal. С TCP проще — протокол сам берёт на себя надёжность и порядок.

Какую задержку добавляет TCP/UDP туннель?

Ориентируйтесь на +1–5 мс поверх базовой задержки сети. Для TCP-сервисов (базы данных, SSH) это практически незаметно. У UDP-приложений (игры, VoIP) overhead чуть выше из-за инкапсуляции датаграмм, но для большинства сценариев он приемлем.

Можно ли пробросить PostgreSQL или Redis через туннель?

Конечно. Запустите fxtunnel tcp 5432 для PostgreSQL или fxtunnel tcp 6379 для Redis — и вы получите публичный адрес, по которому можно подключиться откуда угодно.