← Назад в базу знаний

TikTok Live через IPv6 прокси: почему стрим обрывается на 30-й минуте и как это лечится

TikTok Live через IPv6 прокси: почему стрим обрывается на 30-й минуте и как это лечится

Стрим на TikTok Live через прокси падает. Не сразу — минут через тридцать. Иногда сорок. Картинка замирает, зрители пишут «чё залип», а через несколько секунд соединение рвётся. Перезапуск помогает — и снова полчаса ровной работы. Это не случайность и не «плохой прокси». Это конкретная комбинация таймаутов, MTU и того, как TikTok держит медиапоток через RTMP и QUIC.

Что вообще происходит под капотом

TikTok Live для OBS и мобильных стримеров работает преимущественно через RTMP на входе (порт 1935) и RTMPS (443). Внутри — H.264 видео и AAC аудио, нарезанные на флаги. Со стороны зрителя всё раздаётся через CDN по HLS или QUIC. Стримеру важно только восходящее соединение: TCP-сессия до ingest-сервера.

Когда между OBS и ingest стоит прокси, весь TCP-трафик идёт через него. И вот здесь начинается зоопарк. Прокси может быть SOCKS5, HTTP CONNECT или прозрачный туннель на уровне TUN. Каждый из них по-своему держит соединение, по-своему обрабатывает keepalive и по-своему относится к длинным сессиям.

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

Почему именно 30 минут

Тридцать минут — это не магия TikTok. Это дефолтные таймауты, которые встречаются в куче мест одновременно. NAT-таблица на гипервизоре прокси-сервера часто имеет conntrack timeout 1800 секунд для established TCP. Ровно 30 минут. Если трафик идёт непрерывно, conntrack продлевается. Но RTMP шлёт данные неравномерно: паузы между флагами, keyframe interval, тишина в эфире — всё это создаёт микроокна, где keepalive не проходит.

Второй подозреваемый — сам прокси-демон. Squid по умолчанию рубит соединения по `read_timeout` (15 минут) и `request_timeout`. 3proxy с дефолтным конфигом держит сессию ограниченное время. SOCKS5-серверы на базе dante имеют свой `command_timeout`. Сложите два-три таймаута — получите те самые полчаса.

Третий — MTU. IPv6 не фрагментирует пакеты на маршрутизаторах, только на источнике. Если MTU туннеля 1280, а OBS шлёт пакеты по 1500, они дропаются. TCP ретраит, но медленно. За полчаса накапливается достаточно ретрансмиссий, чтобы TikTok посчитал поток нестабильным и разорвал сессию со стороны ingest.

Как выглядит обрыв в логах

Прежде чем лечить, надо увидеть. В логах OBS при обрыве обычно:

```

21:34:12.441: [rtmp stream: 'simple_stream'] Connection to rtmps://global-rtmp.tiktok.com:443 successful

21:34:12.442: [rtmp stream: 'simple_stream'] Socket send buffer is 65536 bytes

...

22:04:15.113: [rtmp stream: 'simple_stream'] WriteN, RTMP send error 10054 (10038)

22:04:15.113: [rtmp stream: 'simple_stream'] WriteN, RTMP send error 10038 (41)

22:04:15.113: [rtmp stream: 'simple_stream'] Disconnected from rtmps://global-rtmp.tiktok.com:443

```

`10054` — это WSAECONNRESET на Windows. Соединение сброшено удалённой стороной. Смотрим на таймстемпы: 21:34 → 22:04. Ровно 30 минут и 3 секунды. Это не совпадение, это conntrack.

На стороне прокси в этот же момент в `dmesg`:

```

nf_conntrack: table full, dropping packet

```

или в `conntrack -S` растёт `insert_failed`. Гипервизор не успевает продлевать записи, потому что таблица переполнена соседними клиентами.

Что крутить на прокси

Первое — conntrack. На сервере с IPv6-прокси проверьте:

```bash

sysctl net.netfilter.nf_conntrack_tcp_timeout_established

sysctl net.netfilter.nf_conntrack_tcp_timeout_unacknowledged

sysctl net.netfilter.nf_conntrack_max

```

Если `established` = 432000 (5 дней) — хорошо. Если 1800 — вот ваш обрыв. Поднимите до 86400 и увеличьте `nf_conntrack_max` хотя бы до 262144. Это не панацея, но убирает самый частый источник.

Второе — keepalive. TCP keepalive по умолчанию в Linux — 7200 секунд до первого проба. За это время NAT успеет трижды забыть про сессию. Снижайте:

```bash

sysctl -w net.ipv4.tcp_keepalive_time=60

sysctl -w net.ipv4.tcp_keepalive_intvl=15

sysctl -w net.ipv4.tcp_keepalive_probes=4

```

Для IPv6 то же самое, но параметры общие — они в `net.ipv4`, историческое наследие. Работают и на v6-сокетах.

Третье — MTU. Узнайте реальный MTU пути:

```bash

ping6 -M do -s 1452 -c 3 ingest.global.tiktok.com

```

Если пакеты 1480 байт (1452 + 28 заголовков) проходят, а 1500 — нет, у вас PMTU 1480. Ставьте на интерфейсе прокси MTU 1480 или ниже. Для туннелей (WireGuard, GRE) — 1420 безопасно.

Настройка SOCKS5, которая держит стрим

Дефолтный dante рвёт долгие сессии. Конфиг, который работает с RTMP:

```

logoutput: /var/log/socks.log

internal: eth0 port = 1080

external: eth0

clientmethod: none

socksmethod: username

user.privileged: root

user.unprivileged: nobody

client pass {

from: 0.0.0.0/0 to: 0.0.0.0/0

log: error

}

socks pass {

from: 0.0.0.0/0 to: 0.0.0.0/0

command: bind connect udpassociate

log: error

socksmethod: username

}

```

Ключевое — не ставить `timeout.negotiate` и `timeout.io` в маленькие значения. По умолчанию dante держит IO-таймаут 0 (бесконечно), но некоторые сборки ставят 86400. Проверьте. Если 86400 — это сутки, приемлемо. Если меньше — стрим не доживёт.

Для 3proxy аналогично:

```

timeout 86400

connlim 100

noconnect

allow * * *

```

`timeout 86400` — сутки на сессию. `connlim` ограничивает одновременные соединения с одного IP, чтобы один клиент не съел всю таблицу.

Проверка через curl и python

Быстрый тест, что прокси вообще держит долгую сессию — не RTMP, а обычный медленный HTTP. Стримим с сервера бесконечный ответ и читаем через прокси:

```bash

curl -x socks5h://user:pass@[2a01:4f8:1c1c:abcd::1]:1080 \

--max-time 3600 \

-o /dev/null \

-w "connected=%{time_connect} total=%{time_total}\n" \

https://speed.hetzner.de/100MB.bin

```

Если на 1800-й секунде `curl` падает с `Recv failure: Connection reset by peer` — таймаут подтверждён. Если доживает до 3600 — прокси в порядке, копайте в OBS или TikTok.

Для точной диагностики — питон, который держит TCP-сессию и логирует разрывы:

```python

import socket, ssl, time, sys

HOST = "ingest.global.tiktok.com"

PORT = 443

PROXY = ("2a01:4f8:1c1c:abcd::1", 1080)

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)

s.settimeout(30)

s.connect(PROXY)

s.sendall(b"\x05\x01\x00")

if s.recv(2) != b"\x05\x00":

sys.exit("socks handshake failed")

host_b = HOST.encode()

req = b"\x05\x01\x00\x03" + bytes([len(host_b)]) + host_b + PORT.to_bytes(2, "big")

s.sendall(req)

resp = s.recv(10)

if resp[1] != 0:

sys.exit(f"socks connect failed: {resp[1]}")

ctx = ssl.create_default_context()

tls = ctx.wrap_socket(s, server_hostname=HOST)

start = time.time()

try:

while time.time() - start < 3600:

tls.sendall(b"\x00" * 1024)

time.sleep(1)

except Exception as e:

print(f"died after {time.time() - start:.0f}s: {e}")

```

Запускаете, идёте пить кофе. Вернулись через час — либо `died after 1802s`, либо скрипт отработал до конца. Второй вариант означает, что прокси держит, а проблема в чём-то другом.

Кейс: стрим на 1440p через немецкий прокси

Клиент стримил на TikTok Live в 1440p60, битрейт 8000 кбит/с, через IPv6-прокси в Франкфурте. OBS показывал стабильные 0% дропов, но каждые 28-32 минуты — обрыв. Логи OBS: `RTMP send error 10054`. Логи прокси: пусто, никаких ошибок.

Причина оказалась в MTU. Прокси стоял на VPS с WireGuard-туннелем до аплинка, MTU интерфейса 1420, но `wg0` собран с дефолтным 1420, а физический интерфейс — 1500. PMTU discovery по IPv6 работал криво: ICMPv6 Packet Too Big терялся на одном из хопов, OBS не получал уведомление и продолжал шпарить пакетами по 1500. Каждый такой пакет дропался на туннеле, TCP ретраил, RTT рос с 40 мс до 800+ мс. TikTok видел «залипание» и рвал сессию.

Решение: `ip link set wg0 mtu 1380` на прокси и `ip6tables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu`. После этого стрим шёл 6 часов без единого разрыва.

Кейс: conntrack переполнен на общем прокси

Другой клиент — стример с аудиторией 2000+ зрителей, использовал общий прокси на 50 клиентов. Стрим падал стабильно на 30-й минуте. `conntrack -S` на сервере показал `insert_failed: 148293` за сутки. Таблица на 65536 записей, при 50 клиентах с кучей параллельных соединений (браузер, тикток-студия, OBS, чат) — переполнение.

Причина: `nf_conntrack_max` дефолтный, а `tcp_timeout_established` = 432000. Записи висят по пять дней, новые не влезают. TikTok-сессия не может продлить conntrack-запись, ядро её эвиктит, TCP RST уходит в OBS.

Решение: `nf_conntrack_max = 524288`, `tcp_timeout_established = 86400`, плюс `nf_conntrack_tcp_be_liberal = 1` для толерантности к out-of-window пакетам. После правок — 12 часов аптайма стрима без разрывов.

Кейс: RTMPS и TLS-ресессия

Третий случай — стрим через RTMPS (порт 443). OBS подключался, работал 25-35 минут, потом `SSL_write: error 0x0307F07C`. Прокси — HTTP CONNECT на базе squid 5.7.

Squid по умолчанию имеет `read_timeout 15 minutes` и `request_timeout 5 minutes`. Для CONNECT-туннеля применяется `read_timeout`, но с нюансом: если трафик идёт, таймаут продлевается. Проблема была в том, что при низком битрейте (1500 кбит/с на тихом эфире) между пакетами возникали паузы до 200-300 мс, и squid считал соединение «простаивающим».

Плюс сам TikTok периодически инициирует TLS renegotiation на RTMPS — примерно раз в 30 минут. Во время ресессии трафик на пару секунд прекращается. Этого хватало, чтобы squid закрыл туннель.

Решение: `read_timeout 4 hours`, `request_timeout 30 minutes`, `client_idle_pconn_timeout 10 minutes` в `squid.conf`. После этого RTMPS-стримы живут часами.

Таблица: что и где крутить

| Параметр | Дефолт | Для стрима | Где |

|---|---|---|---|

| `nf_conntrack_tcp_timeout_established` | 432000 | 86400 | sysctl прокси |

| `nf_conntrack_max` | 65536 | 524288 | sysctl прокси |

| `tcp_keepalive_time` | 7200 | 60 | sysctl прокси |

| `tcp_keepalive_intvl` | 75 | 15 | sysctl прокси |

| squid `read_timeout` | 15m | 4h | squid.conf |

| dante `timeout.io` | 0 (inf) | 86400 | sockd.conf |

| MTU туннеля | 1500 | 1380-1420 | ip link |

| OBS keyframe interval | 0 (auto) | 2 | OBS settings |

Последняя строка не про прокси, но важна. Keyframe interval 2 секунды означает, что каждые 2 секунды OBS шлёт крупный I-frame, который может не пролезть в MTU и фрагментироваться на уровне TCP. Больше фрагментов — больше шансов потерять один и получить ретрансмиссию. При стриме через прокси всегда ставьте keyframe 2, не auto.

Почему IPv6 хуже IPv4 в этом сценарии

IPv4 умеет фрагментироваться на маршрутизаторах. Некрасиво, но пакет дойдёт. IPv6 фрагментируется только на источнике. Если PMTU не согласован — пакет молча дропается, а источник узнаёт об этом только через ICMPv6 Packet Too Big. Этот ICMP часто фильтруют файрволы. Особенно в облаках.

Плюс IPv6-адресация в прокси-пулах часто реализована через один интерфейс с кучей адресов. Conntrack для каждого адреса свой, но таблица общая. Если пул на 1000 адресов, а `nf_conntrack_max` дефолтный — переполнение гарантировано.

И третье: многие IPv6-прокси-сервисы (включая lexic.ml) выдают клиенту /64 или /48 префикс, и клиент сам выбирает адрес для исходящего соединения. Если адрес меняется в середине сессии — TikTok видит смену источника и рвёт соединение. Проверьте, что прокси-клиент не ротирует адрес при активной сессии.

Чеклист перед стримом

Запустите это на прокси за час до эфира:

```bash

!/bin/bash

sysctl -w net.netfilter.nf_conntrack_max=524288

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400

sysctl -w net.ipv4.tcp_keepalive_time=60

sysctl -w net.ipv4.tcp_keepalive_intvl=15

sysctl -w net.ipv4.tcp_keepalive_probes=4

sysctl -w net.ipv4.tcp_mtu_probing=1

ip link set dev wg0 mtu 1380 2>/dev/null || true

ss -s | grep -i estab

conntrack -C

```

`conntrack -C` покажет текущее число записей. Если оно больше половины `nf_conntrack_max` — срочно поднимайте лимит. `ss -s` покажет общее число established-сокетов. Больше 10 тысяч на слабом VPS — прокси не выдержит стрим.

Затем — тест из раздела про python. Держите сессию 40 минут. Если дожила — можно стримить. Если нет — крутите таймауты, пока не доживёт.

Что делать, если ничего не помогло

Бывает. Тогда вариантов немного. Первый — сменить протокол прокси на что-то с явным keepalive на уровне приложения, например Shadowsocks с `--keepalive 30`. Второй — использовать туннель на уровне L3 (WireGuard) вместо SOCKS. Тогда conntrack работает только на стороне клиента, а прокси видит чистый IP-трафик. Третий — стримить через RTMP напрямую, без TLS, если ingest-сервер это позволяет (некоторые регионы TikTok разрешают порт 1935 без шифрования).

И последнее: если стрим стабильно падает на 30-й минуте, а `conntrack` и таймауты в порядке — проверьте провайдера. Некоторые хостеры режут долгие TCP-сессии на уровне своего edge-роутера, независимо от ваших настроек. Это видно по `traceroute` — если разрыв на хопе провайдера, локальные sysctl не помогут. Меняйте хостера или требуйте от него `tcp_established_timeout` не меньше часа.

✔️Купить прокси