Проброс портов и туннелирование

Рейтинг: 62.3% · 11 голосов
Полный курс по Android Debug Bridge: установка, подключение, shell, логи, dumpsys, автоматизация, root, беспроводная отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
android_roman
Сообщения: 45
Зарегистрирован: 11 май 2026, 05:31

Проброс портов и туннелирование

Сообщение android_roman »

АкадемияADB: Android Debug BridgeГлава 19 из 29
Оглавление курса (29)
  1. Введение в Android Debug Bridge
  2. Установка и настройка рабочей среды
  3. Подключение устройства (проводное и беспроводное)
  4. Базовые команды ADB и управление сервером
  5. Команды состояния и перезагрузки
  6. Навигация по файловой системе
  7. Управление пакетами приложений
  8. Логирование с помощью logcat
  9. Системные дампы и диагностика (dumpsys)
  10. Анализ производительности в реальном времени
  11. Эмуляция ввода (input)
  12. Управление Activity и Intent (am)
  13. Работа с оконным менеджером (wm)
  14. Захват экрана и запись видео
  15. Root-доступ и его применение
  16. Модификация системных настроек через settings
  17. Команды для поставщиков контента (content)
  18. Резервное копирование и восстановление (backup)
  19. Проброс портов и туннелирование (вы здесь)
  20. Беспроводная отладка (Wi-Fi)
  21. Взаимодействие с эмуляторами
  22. Написание скриптов на Bash/CMD/PowerShell
  23. ADB в языках программирования
  24. Автоматизация тестирования с ADB
  25. Безопасность и лучшие практики
  26. ADB на Android TV, Wear OS и IoT
  27. Восстановление и низкоуровневые операции
  28. Расширенные возможности оболочки и инструменты
  29. Отладка самого ADB и устранение неполадок
ADB умеет гонять через себя произвольные TCP-соединения. Тот же канал, по которому ходят shell-команды и логи (USB или Wi-Fi, главы 3 и 20), переносит байты любого протокола поверх TCP. За это отвечают две команды: forward (хост -> устройство) и reverse (устройство -> хост). Без них не обходится отладка WebView, React Native, локальных API и половина инструментов вроде scrcpy.

Как это устроено:

ADB-сервер на хосте (порт 5037, глава 4) держит транспорт к демону adbd на устройстве. Команда forward просит сервер открыть слушающий сокет на хосте и переправлять каждое входящее соединение на указанный сокет устройства. reverse делает зеркально: adbd слушает порт на устройстве и переправляет соединения на хост. Это не VPN и не маршрутизация, туннелируются только те сокеты, которые вы явно указали. UDP не поддерживается вообще, только TCP и unix-сокеты.

adb forward, с хоста на устройство:

Синтаксис: adb forward LOCAL REMOTE. Первый аргумент всегда сторона хоста, второй сторона устройства.

Код: Выделить всё

adb forward tcp:9090 tcp:8080
Теперь всё, что прилетит на 127.0.0.1:9090 хоста, окажется на порту 8080 устройства. Свежие platform-tools печатают номер локального порта в stdout, старые версии молчали. Проверим на живом примере: поднимем HTTP-сервер в Termux на устройстве и дернем его с хоста.

Код: Выделить всё

adb shell
$ python -m http.server 8080

# в соседнем терминале на хосте:
adb forward tcp:9090 tcp:8080
curl -I http://127.0.0.1:9090/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.11
Кроме tcp: на стороне устройства доступны и другие типы сокетов:

Код: Выделить всё

adb forward tcp:9222 localabstract:chrome_devtools_remote
adb forward tcp:8700 jdwp:14231
adb forward tcp:9999 localfilesystem:/data/local/tmp/app.sock
localabstract, это абстрактные unix-сокеты Linux, на них висят DevTools браузеров и WebView. jdwp: подключает Java-отладчик к процессу по pid (список отлаживаемых процессов выдаёт команда adb jdwp). По той же схеме работает scrcpy: поднимает туннель к своему localabstract-сокету и гонит через него видеопоток.

Если конкретный локальный порт не важен, отдайте выбор системе через tcp:0, adb напечатает выделенный порт:

Код: Выделить всё

adb forward tcp:0 tcp:8080
41273
Когда устройств подключено несколько, обязательно указывайте серийник, иначе получите error: more than one device/emulator:

Код: Выделить всё

adb -s RF8M33XQHTK forward tcp:9090 tcp:8080
adb forward --list и управление правилами:

Все активные правила живут в adb-сервере и видны из любого терминала:

Код: Выделить всё

adb forward --list
RF8M33XQHTK tcp:9222 localabstract:chrome_devtools_remote
emulator-5554 tcp:9090 tcp:8080
В первой колонке серийник устройства, дальше локальный и удалённый сокеты. Снять правило:

Код: Выделить всё

adb forward --remove tcp:9090
adb forward --remove-all
По умолчанию повторный forward на уже занятый правилом локальный порт молча перепривязывает его к новой цели. Если в скриптах такое поведение опасно, добавьте --no-rebind:

Код: Выделить всё

adb forward --no-rebind tcp:9090 tcp:8081
error: cannot rebind existing socket
adb reverse, с устройства на хост:

Зеркальная команда, и порядок аргументов тоже зеркальный: сначала сокет устройства, потом сокет хоста.

Код: Выделить всё

adb reverse tcp:8081 tcp:8081
Теперь приложение на устройстве, постучавшись на 127.0.0.1:8081, попадёт на порт 8081 вашей машины. Работает на Android 5.0 (API 21) и новее, на 4.x команда вернёт error: closed.

Управление полностью аналогично forward:

Код: Выделить всё

adb reverse --list
UsbFfs tcp:8081 tcp:8081

adb reverse --remove tcp:8081
adb reverse --remove-all
В первой колонке списка не серийник, а имя транспорта на стороне устройства (UsbFfs для USB-подключения), не пугайтесь.

Сценарий: доступ к локальному серверу:

Классика, React Native. Metro-бандлер слушает 8081 на машине разработчика:

Код: Выделить всё

adb reverse tcp:8081 tcp:8081
и приложение на физическом устройстве тянет JS-бандл с localhost:8081 так, будто сервер запущен на самом телефоне. То же самое с любым dev-сервером: подняли фронт на 127.0.0.1:3000, выполнили adb reverse tcp:3000 tcp:3000, открыли http://127.0.0.1:3000 в мобильном Chrome.

Два бонуса по сравнению с подходом "подключи телефон к тому же Wi-Fi и зайди по IP". Во-первых, reverse работает и по USB, общая сеть не нужна (актуально в офисах, где гостевой Wi-Fi изолирует клиентов друг от друга). Во-вторых, dev-сервер продолжает слушать loopback, его не надо выставлять на 0.0.0.0 на всю офисную сеть.

На эмуляторе есть альтернатива: специальный адрес 10.0.2.2 ведёт на loopback хоста без всяких туннелей (глава 21). На физическом устройстве такого адреса нет, там reverse безальтернативен.

Сценарий: отладка WebView через Chrome DevTools:

Простой путь: открыть chrome://inspect/#devices в десктопном Chrome. Он сам общается с adb-сервером и сам поднимает нужные форварды, от вас требуется только включённая отладка по USB и вызов WebView.setWebContentsDebuggingEnabled(true) в приложении. Без этого вызова WebView не открывает отладочный сокет, в release-сборках его обычно и не делают.

Ручной путь нужен для CI, headless-инструментов и нестандартных браузеров. Сначала ищем отладочные сокеты:

Код: Выделить всё

adb shell "cat /proc/net/unix | grep -a devtools"
0000000000000000: 00000002 00000000 00010000 0001 01 21931 @chrome_devtools_remote
0000000000000000: 00000002 00000000 00010000 0001 01 88412 @webview_devtools_remote_14231
chrome_devtools_remote принадлежит самому Chrome, webview_devtools_remote_<pid> относится к WebView конкретного процесса. Пробрасываем и проверяем:

Код: Выделить всё

adb forward tcp:9222 localabstract:webview_devtools_remote_14231
curl http://127.0.0.1:9222/json/version
{
   "Android-Package": "ru.example.shop",
   "Browser": "Chrome/140.0.7339.51",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (Linux; Android 15; ...)"
}
Запрос http://127.0.0.1:9222/json/list отдаст открытые страницы с готовыми ссылками на DevTools-фронтенд. К этому же порту цепляются Puppeteer и Playwright по протоколу CDP. Помните, что pid меняется при каждом перезапуске приложения, имя сокета меняется вместе с ним, и форвард придётся пересоздавать.

Время жизни туннелей:

Правила хранятся в adb-сервере и не переживают adb kill-server, переподключение кабеля, обрыв Wi-Fi-сессии и перезагрузку устройства. Никакой автоматики восстановления нет. В скриптах и CI ставьте forward/reverse в начале каждого прогона и не рассчитывайте, что туннель остался со вчера. Текущее состояние всегда покажет adb forward --list.

Ограничения и безопасность:

Туннелируются только TCP и unix-сокеты. UDP не поддерживается в принципе, для игр и стриминга по UDP проброс через adb не вариант.

Слушающий сокет forward на хосте привязан к 127.0.0.1. С одной стороны это защита, снаружи к нему не подойти. С другой стороны, на общей build-машине любой локальный пользователь может подключиться к вашему проброшенному порту, аутентификации в туннеле нет. Проброшенный DevTools-сокет даёт полный контроль над страницей вместе с куками и сессиями, помните об этом на shared-серверах.

adb reverse открывает порт на устройстве сразу для всех приложений. Любая установленная программа может постучаться на 127.0.0.1:8081 и достать ваш хостовый сервис. Не держите reverse на сервисы без авторизации (локальные базы, внутренние админки) на устройствах, куда ставят что попало.

Порты ниже 1024 на хосте требуют прав администратора, обычный adb forward tcp:80 на Linux/macOS упадёт с отказом в привязке. Берите порты повыше.

Запуск adb-сервера с флагом -a (слушать все интерфейсы) выставляет наружу и сам сервер, и форварды. Не делайте так в общих сетях, для удалённого доступа к туннелям существует ssh.

Про беспроводную отладку: legacy-режим adb tcpip 5555 (глава 3) не шифрует трафик, содержимое всех туннелей в нём ходит по Wi-Fi открытым текстом. Современная беспроводная отладка Android 11+ с сопряжением использует TLS, подробности в главе 20.

Частые ошибки:

error: cannot bind listener: Address already in use. Локальный порт занят другим процессом. Найдите кто держит (lsof -i :9090 на Linux/macOS, netstat -ano | findstr 9090 на Windows) или возьмите tcp:0.

Форвард создался, а соединение сбрасывается. adb не проверяет, что на стороне устройства кто-то слушает, ошибка вылезет только при первом подключении. Проверьте слушателей на устройстве:

Код: Выделить всё

adb shell netstat -tln
Пустой список в /json/list у WebView. Либо приложение не вызвало setWebContentsDebuggingEnabled(true), либо вы пробросили сокет со старым pid после перезапуска приложения.

error: closed на adb reverse. Почти всегда устройство с Android младше 5.0. Там остаётся общий Wi-Fi с прямым IP или эмулятор с 10.0.2.2.

Туннель пропал после переподключения устройства. Это норма, смотрите раздел про время жизни и пересоздавайте правила в скрипте.
👍1 ❤️3 🔥 😄 🤔
✔ Лучший ответ сформирован автоматически — wiktionary
android_roman писал(а):Свежие platform-tools печатают номер локального порта в stdout, старые версии молчали. вот это коварный момент. у нас в CI-образе старый platform-tools, скрипт парсил вывод forward и падал на пустой строке. с какой примерно версии вывод появился? чтобы понять, обновлять образ или просто брать порт из своей же переменной
Перейти к ответу →
Аватара пользователя
coder_marina
Сообщения: 3
Зарегистрирован: 16 май 2026, 15:03

Re: Проброс портов и туннелирование

Сообщение coder_marina »

android_roman писал(а):Любая установленная программа может постучаться на 127.0.0.1:8081 и достать ваш хостовый сервис.
а ограничить это как-то можно? у нас тестовые телефоны общие, на них чего только не наставлено. хотелось бы reverse только для своего приложения, неужели в adb нет фильтра по uid или хотя бы по пакету?
👍3 ❤️ 🔥 😄 🤔
Аватара пользователя
deepgrind
Сообщения: 1
Зарегистрирован: 13 май 2026, 15:14

Re: Проброс портов и туннелирование

Сообщение deepgrind »

по поводу chrome://inspect добавлю из опыта: если список устройств пустой, первым делом проверьте что у вас не крутятся два разных adb, системный и тот что в Android Studio. они перезапускают сервер друг под другом и форварды слетают молча. убил студийный, оставил один из platform-tools, все появилось.
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
terraform2
Сообщения: 1
Зарегистрирован: 30 май 2026, 06:26

Re: Проброс портов и туннелирование

Сообщение terraform2 »

а с UDP совсем тупик? отлаживаю мультиплеер, сервер локальный, клиент на телефоне шлет датаграммы. пробовал кто-нибудь заворачивать udp в tcp через socat с двух сторон туннеля, или проще не мучиться и поднять сервер на машине с белым IP?
👍1 ❤️1 🔥 😄 🤔2
Аватара пользователя
wiktionary
Сообщения: 1
Зарегистрирован: 15 май 2026, 17:39

Re: Проброс портов и туннелирование

Сообщение wiktionary »

✔ Лучший ответ — сформирован автоматически
android_roman писал(а):Свежие platform-tools печатают номер локального порта в stdout, старые версии молчали.
вот это коварный момент. у нас в CI-образе старый platform-tools, скрипт парсил вывод forward и падал на пустой строке. с какой примерно версии вывод появился? чтобы понять, обновлять образ или просто брать порт из своей же переменной
👍 ❤️ 🔥 😄 🤔1
Ответить
← Предыдущая глава
Резервное копирование и восстановление (backup)
Следующая глава →
Беспроводная отладка (Wi-Fi)

Все главы курса «ADB: Android Debug Bridge»

Поделиться темой: ✈ Telegram VK

Вернуться в «ADB: Android Debug Bridge»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость