Введение в v2ray транспорты
Материал из Все о VPN, прокси и свободном интернете
Введение в v2ray транспорты
Оригинал: https://github.com/mmmray/wall-documents/blob/main/transports.md
Давайте вместе с Вами исследуем тему транспортов в xray-core, так как видимо сухая техническая документация с указанием параметров, не дает общее представление конечному пользователю. Вообще, транспорты в нашем ядре — это буквально все что угодно, что может эмулировать (или оборачивать) двунаправленное соединение между сервером и клиентом. TCP-сокеты являются именно такими, но это не обязательно должен быть TCP или даже основанный на TCP. Это должно быть что-то, что передает байты по порядку от клиента к серверу без их потери и наоборот. После предоставления этого канала поверх него могут работать Vmess, VLESS или Trojan. Или что-то совершенно другое.
Наш первый транспорт Мы будем использовать некоторые команды Linux, чтобы показать, что могут делать транспорты. Начнем с чего-то простого. Откройте терминал и введите это: nc -l localhost 6003 # сервер и в другом терминале: nc localhost 6003 # клиент Напишите несколько строк в одном терминале и наблюдайте, как они появляются в другом. Это работает в обоих направлениях. Эта команда эквивалентна “TCP транспорту” в нашем ядре. Вы можете заменить хост и порт в команде client, чтобы проверить, открыт ли TCP-порт.
WebSocket WebSocket — это протокол на основе HTTP, предназначенный для выполнения той же задачи, что и TCP-сокет. Основное различие заключается в следующем: WebSocket добавляет большое рукопожатие на основе HTTP в начале соединения. Это полезно для кукисов, аутентификации, обслуживания нескольких “WebSocket-сервисов” под разными HTTP-путями и для обеспечения того, чтобы веб-сайт мог открывать соединения только с собственным источником, а не с случайными хостами. Для целей создания транспортов некоторые из этих функций, особенно маршрутизация на основе пути, очень полезны, но дополнительный обмен данными в начале добавляет дополнительную задержку. WebSocket не передает байты, а “сообщения”. Для наших целей это означает, что вместо Write("hello world") и отправки hello world (как в TCP), фактически отправляется <frame header>hello world. Для целей создания транспортов этот дизайн является ненужной нагрузкой. Причина, по которой мы миримся с этими аспектами, заключается в том, что некоторые CDN могут напрямую пересылать WebSocket. Если nc фактически является TCP-транспортом, то такие инструменты, как websocat или wscat, фактически являются WebSocket-транспортами. На самом деле, эти инструменты очень полезны для проверки, правильно ли слушает WebSocket на определенном пути: curl https://example.com # example.com является действительным HTTP-сервисом... websocat wss://example.com # ...но фактически не является WebSocket! Это не работает Чтобы получить локальный тестовый сервер, как в предыдущем примере с nc, вы можете сделать это: websocat -s 6003 # сервер websocat ws://localhost:6003 # клиент Еще раз, вы можете отправлять случайные строки текста туда и обратно. Поскольку WebSocket — это просто HTTP/1.1, а HTTP/1.1 — это просто TCP, мы можем даже направить websocat на nc, чтобы вывести начальное рукопожатие: nc -l localhost 6003 # сервер websocat ws://localhost:6003 # клиент Когда вы выполните вторую команду, она фактически выведет: GET / HTTP/1.1 Host: localhost:6003 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg== Это часть нагрузки WebSocket. Клиент все еще ждет ответа. Давайте также извлечем ответ. Прекратите обе команды с помощью Ctrl-C и запустите websocat -s 6003 как сервер. Возьмите приведенный выше текст и отправьте его напрямую с помощью nc: echo 'GET / HTTP/1.1 Host: localhost:6003 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg== ' | unix2dos | nc localhost 6003 | cat -v echo просто выводит данные unix2dos преобразует окончания строк, поскольку HTTP/1.1 ожидает \r\n вместо \n nc отправляет данные на сервер и выводит ответ cat -v делает специальные символы (или, скорее, байты, не являющиеся символами) видимыми. Вы увидите ответ: HTTP/1.1 101 Switching Protocols^M Sec-WebSocket-Accept: HKN6nOSb0JT0jWhszYuKJPUPpHg=^M Connection: Upgrade^M Upgrade: websocket^M
После этого вы можете отправлять данные с сервера на клиент, вводя их в окне терминала сервера. Введите hello в сервер websocat, и наблюдайте на клиенте: M-^A^Fhello Мусор в начале — это фрейм сообщения WebSocket. Отправка данных с клиента на сервер не работает, поскольку WebSocket ожидает, что данные будут обернуты в этот фрейм сообщений.
WebSocket 0-RTT (избавляемся от накладных расходов на рукопожатие) Где TCP просто выполняет свое рукопожатие, WebSocket отправляет HTTP-запрос и ждет ответа. Эта дополнительная задержка очень заметна, особенно в следующем потоке: Выполнить TCP рукопожатие Отправить HTTP-запрос Ждать HTTP-ответа и прочитать его Отправить инструкцию VLESS для подключения к какому-то веб-сайту pornhub.com Ждать ответа и прочитать его Отправьте немного данных, подождите, отправьте еще немного данных, снова подождите. Было бы быстрее отправить много данных, а затем подождать много данных: Выполнить TCP рукопожатие (его не избежать… пока…) Отправить HTTP-запрос вместе с первой инструкцией VLESS для подключения к pornhub.com Ждать HTTP-ответа и первых байтов тела одновременно Если мы сможем это достичь, это будет на один RTT меньше. Xray и Sing-Box реализуют эту идею под названием Early Data или иногда “0-RTT”. Ранние данные — это просто любые данные, которые клиент хочет записать на шаге 2. Sing-Box по умолчанию отправляет это как часть URL, но может быть настроен на использование любого имени заголовка (значение в base64). В Xray это всегда отправляется как заголовок Sec-WebSocket-Protocol. Существует довольно неочевидная причина для этого под названием Browser Dialer для этого конкретного выбора заголовка. Можно сказать, что отправка этих данных в URL более совместима с HTTP-проксами, но заголовки имеют большую емкость для больших данных. Это в основном решает проблемы задержки, связанные с WebSocket.
HTTPUpgrade (избавляемся от накладных расходов на фреймирование данных) Помните? Когда вы отправляете hello в WebSocket, фактически передается: M-^A^Fhello Что за мусор впереди? Это просто трата полосы пропускания. Мы ранее говорили, что стандарт WebSocket этого требует. Но на самом деле оказывается, что многие CDN не заботятся о том, если вы отправляете фактические данные WebSocket через соединение WebSocket. После первого HTTP-запроса и HTTP-ответа, кажется, что сокет можно использовать напрямую без каких-либо накладных расходов вообще. Вся идея HTTPUpgrade заключается в этом. Изначально этот вид транспорта был разработан Tor под названием HTTPT, некоторое время после того, как v2ray добавил WebSocket. Позже v2fly перенес его как новый транспорт, затем он был скопирован в Xray.
Таким образом, передача становится такой. От клиента: GET / HTTP/1.1 Host: localhost:6003 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg== Затем ответ от сервера: HTTP/1.1 101 Switching Protocols Sec-WebSocket-Accept: HKN6nOSb0JT0jWhszYuKJPUPpHg= Connection: Upgrade Upgrade: websocket …и затем он используется напрямую как обычное TCP-соединение, как в первом примере с nc. HTTPUpgrade 0-RTT (избавляемся от обеих проблем) Это работает так же, как WebSocket 0-RTT. Теперь обе проблемы WebSocket устранены: Накладные расходы на рукопожатие (в основном) и накладные расходы на сообщения (полностью).
GRPC TODO SplitHTTP TODO