Как это устроено:
ADB-сервер на хосте (порт 5037, глава 4) держит транспорт к демону adbd на устройстве. Команда forward просит сервер открыть слушающий сокет на хосте и переправлять каждое входящее соединение на указанный сокет устройства. reverse делает зеркально: adbd слушает порт на устройстве и переправляет соединения на хост. Это не VPN и не маршрутизация, туннелируются только те сокеты, которые вы явно указали. UDP не поддерживается вообще, только TCP и unix-сокеты.
adb forward, с хоста на устройство:
Синтаксис: adb forward LOCAL REMOTE. Первый аргумент всегда сторона хоста, второй сторона устройства.
Код: Выделить всё
adb forward tcp:9090 tcp:8080Код: Выделить всё
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Код: Выделить всё
adb forward tcp:9222 localabstract:chrome_devtools_remote
adb forward tcp:8700 jdwp:14231
adb forward tcp:9999 localfilesystem:/data/local/tmp/app.sockЕсли конкретный локальный порт не важен, отдайте выбор системе через tcp:0, adb напечатает выделенный порт:
Код: Выделить всё
adb forward tcp:0 tcp:8080
41273Код: Выделить всё
adb -s RF8M33XQHTK forward tcp:9090 tcp:8080Все активные правила живут в 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Код: Выделить всё
adb forward --no-rebind tcp:9090 tcp:8081
error: cannot rebind existing socketЗеркальная команда, и порядок аргументов тоже зеркальный: сначала сокет устройства, потом сокет хоста.
Код: Выделить всё
adb reverse tcp:8081 tcp:8081Управление полностью аналогично forward:
Код: Выделить всё
adb reverse --list
UsbFfs tcp:8081 tcp:8081
adb reverse --remove tcp:8081
adb reverse --remove-allСценарий: доступ к локальному серверу:
Классика, React Native. Metro-бандлер слушает 8081 на машине разработчика:
Код: Выделить всё
adb reverse tcp:8081 tcp:8081Два бонуса по сравнению с подходом "подключи телефон к тому же 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Код: Выделить всё
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; ...)"
}Время жизни туннелей:
Правила хранятся в 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 -tlnerror: closed на adb reverse. Почти всегда устройство с Android младше 5.0. Там остаётся общий Wi-Fi с прямым IP или эмулятор с 10.0.2.2.
Туннель пропал после переподключения устройства. Это норма, смотрите раздел про время жизни и пересоздавайте правила в скрипте.