Discord через IPv6 прокси: нестабильность WebSocket и обходные пути
Содержание
Discord держит WebSocket-соединения до 24 часов. Это не баг, а фича — так меньше нагрузка на шлюз. Но когда за спиной IPv6-прокси, эти долгоживущие коннекты превращаются в источник головной боли. Разберём, почему Discord через прокси начинает «моргать» и что с этим делать.
Анатомия нестабильности
Discord использует несколько шлюзов: `gateway.discord.gg`, `gateway-us-east-1-a.discord.gg` и так далее. Каждый держит TCP-соединение поверх WebSocket с TLS. Проблема в том, что прокси-серверы часто имеют таймауты на неактивные соединения. Стандартные значения — 60-300 секунд. Discord же отправляет heartbeat каждые 41250 мс (41.25 секунды). Казалось бы, запас есть. Но на практике всё сложнее.
Когда вы подключаетесь через IPv6-прокси, трафик идёт через NAT64 или туннель. Промежуточные устройства — файрволы, балансировщики, сам прокси — могут сбрасывать соединения по своим правилам. Особенно зверствуют stateful-файрволы: они удаляют записи о соединении, если не видят трафика определённое время. Heartbeat раз в 41 секунду — это не панацея.
Пример: прокси на базе Linux с `nf_conntrack_udp_timeout` и `nf_conntrack_tcp_timeout` по умолчанию. TCP-таймаут — 43200 секунд, вроде всё ок. Но если прокси использует `keepalive` на уровне ядра с параметром `tcp_keepalive_time = 7200`, то без данных соединение живёт 2 часа. Discord heartbeat не спасает — это данные прикладного уровня, а не TCP-level keepalive.
Симптомы и диагностика
Классические признаки: сообщения отправляются с задержкой 5-10 секунд, голосовые каналы «заикаются», статус «онлайн» сменяется на «не в сети» каждые несколько минут. В логах прокси — куча `TCP RST` от Discord-серверов.
Проверка: откройте терминал и посмотрите, что происходит с соединением.
```bash
ss -tnp | grep discord
```
Если видите `ESTAB` и через минуту `CLOSE-WAIT` или просто пропажу — соединение сбрасывается. Ещё один способ — tcpdump на интерфейсе прокси:
```bash
tcpdump -i eth0 host gateway.discord.gg -nn
```
Ищите пакеты с флагами `R` или последовательности `FIN, ACK` без ответа. Если RST приходит от Discord — это их политика. Если от вашего прокси — проблема локальная.
Почему IPv6 усугубляет ситуацию
IPv6 не имеет NAT в классическом понимании. Но прокси-сервисы часто используют NPTv6 (Network Prefix Translation) или просто маршрутизируют трафик. Проблема в другом: многие прокси-сервера настраивают таймауты для IPv6-соединений более агрессивно, чем для IPv4. Причина — ограниченные таблицы соединений в ядре.
Конкретный пример: прокси на Linux с `net.ipv6.conf.all.max_addresses` и настройками `net.netfilter.nf_conntrack_buckets`. При большом количестве IPv6-адресов таблица соединений забивается быстрее. Старые записи вытесняются, соединения рвутся.
Ещё один момент — MTU. IPv6 имеет минимальный MTU 1280 байт, но туннели (6in4, Teredo, gif) могут фрагментировать пакеты. Discord отправляет кадры до 1400 байт. Если прокси не умеет корректно обрабатывать фрагментацию — пакеты теряются, WebSocket начинает захлёбываться.
Настройка keepalive на стороне клиента
Первый обходной путь — заставить клиент отправлять TCP keepalive. Discord-клиент не даёт такой настройки, но можно использовать системные параметры.
Для Linux:
```bash
sudo sysctl -w net.ipv4.tcp_keepalive_time=60
sudo sysctl -w net.ipv4.tcp_keepalive_intvl=10
sudo sysctl -w net.ipv4.tcp_keepalive_probes=3
```
Для IPv6 аналогично:
```bash
sudo sysctl -w net.ipv6.tcp_keepalive_time=60
sudo sysctl -w net.ipv6.tcp_keepalive_intvl=10
sudo sysctl -w net.ipv6.tcp_keepalive_probes=3
```
Эти параметры заставят ядро отправлять пустые ACK-пакеты каждые 60 секунд. Discord-сервер видит активность и не рвёт соединение. Но есть нюанс: не все прокси пропускают такие пакеты. Некоторые считают их мусором и отбрасывают.
Более надёжный вариант — использовать `socat` как прокси-обёртку. Он умеет держать соединение активным:
```bash
socat TCP6:[gateway.discord.gg]:443 TCP4:127.0.0.1:8080,keepalive,keepaliveinterval=30
```
Здесь `keepaliveinterval=30` — отправка keepalive каждые 30 секунд. Это работает на уровне приложения, а не ядра, поэтому прокси не сможет отфильтровать.
Переподключение с экспоненциальной задержкой
Discord официально рекомендует переподключаться с экспоненциальной задержкой. Клиент делает это сам, но вы можете написать скрипт, который будет перезапускать WebSocket при обрыве. Python-пример:
```python
import asyncio
import websockets
import time
async def connect_with_retry():
delay = 1
max_delay = 60
while True:
try:
async with websockets.connect(
'wss://gateway.discord.gg/?v=9&encoding=json',
extra_headers={'Origin': 'https://discord.com'}
) as ws:
delay = 1
await ws.send('{"op":2,"d":{"token":"YOUR_TOKEN","intents":513}}')
async for message in ws:
print(message)
except Exception as e:
print(f"Connection error: {e}")
await asyncio.sleep(delay)
delay = min(delay * 2, max_delay)
asyncio.run(connect_with_retry())
```
Заметьте: `delay` растёт с 1 до 60 секунд. Это снижает нагрузку на прокси и уменьшает вероятность того, что Discord временно забанит IP за частые переподключения.
Тонкая настройка прокси
Если прокси ваш — настраивайте таймауты правильно. Для nginx-стрима (если используете его как прокси):
```nginx
stream {
upstream discord {
server gateway.discord.gg:443;
}
server {
listen 8080;
proxy_pass discord;
proxy_timeout 3600s;
proxy_socket_keepalive on;
}
}
```
`proxy_timeout 3600s` — час бездействия. `proxy_socket_keepalive on` — включает TCP keepalive на уровне сокета. Это решает проблему для большинства случаев.
Для haproxy:
```
backend discord
server gateway gateway.discord.gg:443 check inter 5s
timeout server 1h
timeout tunnel 1h
```
`timeout tunnel` критичен для WebSocket — он не даёт haproxy закрыть соединение после завершения HTTP-запроса.
MTU и фрагментация
Discord шлёт голосовые данные в реальном времени. Если MTU на прокси меньше, чем на клиенте, пакеты фрагментируются. Для UDP это потеря — Discord просто отбрасывает фрагменты, которые пришли с задержкой.
Проверьте MTU на интерфейсе:
```bash
ip link show
```
Если видите `mtu 1500`, а туннель даёт 1280 — проблема. Исправление:
```bash
sudo ip link set dev tun0 mtu 1280
```
Для WireGuard:
```bash
sudo ip link set dev wg0 mtu 1280
```
Заниженный MTU — не катастрофа. 1280 байт достаточно для Discord, просто скорость чуть ниже.
Кейс: корпоративный файрвол
Пример: компания использует Cisco ASA с инспекцией TLS. Файрвол перехватывает сертификаты, ломает WebSocket. Discord-клиент видит несоответствие сертификата и рвёт соединение каждые 5 минут. Решение — добавить домены Discord в исключения SSL-инспекции:
```
gateway.discord.gg
gateway-us-east-1-a.discord.gg
gateway-us-east-1-b.discord.gg
gateway-us-east-1-c.discord.gg
gateway-us-east-1-d.discord.gg
gateway-us-east-1-e.discord.gg
gateway-us-east-1-f.discord.gg
```
Или использовать `ssl-bypass` на ASA. После этого соединения стабильны.
Кейс: перегруженный NAT
Другой пример: домашний роутер с NAT на 5000 соединений. Discord + браузер + игры — таблица забита. IPv6-прокси добавляет ещё 500-1000 записей. Роутер начинает выбрасывать старые записи. Решение — перейти на прокси с выделенным IPv6-адресом, который не требует NAT. Сервис [lexic.ml](https://lexic.ml) предоставляет такие адреса. Настройка:
```bash
ip -6 addr add 2001:db8::1/64 dev eth0
ip -6 route add default via 2001:db8::ffff
```
После этого соединения идут напрямую, без NAT-таблиц. Таймауты перестают играть роль.
Кейс: мобильный оператор
На мобильных сетях операторы часто рвут длительные соединения. Среднее время жизни TCP-соединения — 10-15 минут. Discord heartbeat не спасает. Пользователи жалуются на «дисконнекты» каждые 10 минут.
Решение — использовать UDP-туннель вместо TCP. WireGuard поверх UDP живёт дольше, потому что операторы реже сбрасывают UDP-сессии. Настройка на клиенте:
```bash
sudo apt install wireguard
sudo wg-quick up wg0
```
Пинг до шлюза Discord через WireGuard — 30-40 мс против 50-60 мс через TCP-прокси. Стабильность — 99.9% против 95%.
Что в итоге
Стабильный Discord через IPv6-прокси — это комбинация настроек. Keepalive на клиенте, правильные таймауты на прокси, корректный MTU. Если прокси арендованный — проверяйте, какие таймауты он использует. Некоторые сервисы режут соединения на 60 секунд, и никакой heartbeat не поможет.
Перед покупкой прокси спросите поддержку о политике в отношении длительных соединений. Если ответ «не знаем» — ищите другого провайдера. Стабильность — это не магия, а инженерная работа.