Сбой соединения Kurento WebRTC в ~30% случаев
Я провел дни, выискивая проблему с подключением без какой-либо удачи. Я пытаюсь реализовать относительно простой one2one Call с Kurento.
Ниже вы найдете журнал отладки Kurento для случая, когда соединение могло быть установлено, и случая, когда соединение не удалось.
Если вам нужно больше журналов (например, клиентов, сигнального сервера, tcpdumps или журналов трассировки Kurento, просто дайте мне знать, и я предоставлю!)
Любая помощь или новый вклад с благодарностью!
Описание проблемы:
Примерно в 30% случаев соединение WebRTC не может быть установлено. К сожалению, мне не хватает какого-либо паттерна, когда Соединение может быть установлено, а когда нет, оно кажется совершенно случайным. Я нахожусь в той же сети, использую те же устройства, использую тот же сервер TURN, использую тот же протокол сигнализации, но в 30% случаев соединение не может быть установлено.
Когда я запускаю приложение локально, кажется, что оно работает намного надежнее, соединение может быть установлено почти в 100% случаев (или, может быть, даже в 100% случаев, я столько раз тестировал, что терял трек). Я настроил инфраструктуру локально с помощью Docker и запускаю различные контейнеры (TURN, Kurento, Signaling) в отдельных сетях, чтобы имитировать производственное развертывание.
Такое же поведение мы наблюдаем в нашей среде разработки и производства. В нашей среде разработки у нас абсолютно нет брандмауэров, так что, похоже, это не проблема.
Что я пытался найти причину проблемы:
В основном я сравнивал журналы дел, которые работали, и дел, которые не работали, но я не смог найти какой-либо существенной разницы между ними, которая могла бы указать мне на проблему.
Я проверил соединение WebRTC через сервер TURN (с Firefox и флагом force_relay) и напрямую через Kurento, но в обоих случаях соединение обрывается в ~30% случаев.
Я попытался отфильтровать всех кандидатов ICE, которые не являются кандидатами на ретрансляцию.
Я прослушал трафик между нашим сервером сигнализации (который также контролирует Kurento) и Kurento, чтобы увидеть какие-либо различия в обмене сообщениями JSON RPS, но они, по сути, одинаковы.
Я проверил наш сервер STUN и TURN с помощью этого инструмента: https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/ и я получил как серверные рефлексы, так и ретрансляторы, которые выглядят правильно
Я обнаружил трафик от клиентов успешного и неудачного соединения, но смог заметить существенную разницу
Я упростил медиа-конвейер Kurento (без записи, без хабов), но поведение такое же
Я использовал разные браузеры (Chrome, Firefox и нативная реализация iOS), но поведение такое же
Куренто отлаживает логи случая, когда соединение может быть установлено:
https://gist.github.com/omnibrain/2bc7ad54f626d278d3c8bac29767ac4c
Kurento отладки журналов случая, когда соединение не может быть установлено:
https://gist.github.com/omnibrain/f7caee04a5c6d77ea22a9ccfa95dd825
2 ответа
После нескольких дней отладки и почти безумия мы наконец нашли причину нашей проблемы:
Мы использовали Swift-клиент Socket.IO и серверную реализацию Java Netty Socket.IO Socket.IO. Клиент (iOS) использует длинный опрос для связи с сервером (Java). Оказывается, что сервер Netty Socket.IO выполняет URL-декодирование полезной нагрузки длинного опроса клиента Swift Socket.IO, но клиент Swift Socket.IO фактически не кодирует его URL-адресом. Это означало, что каждое "+", которое было отправлено с клиента Swift Socket.IO, было заменено на " " (пробел) на сервере. Почему это проблема? Потому что предложение SDP клиента содержит ufrag, который может содержать знак плюс! Таким образом, если SDP содержал "+", он был заменен на сервере пробелом, что приводило к сбою эхо-запросов STUN из-за невозможности проверки целостности сообщения.
Глядя на ваши следы, ваш рабочий случай выбирает кандидата 10, затем выбирает кандидата 7, нерабочий выбирает только кандидата 10.
kurento_logs_webrtc_working.txt
New candidate pair selected, local: 'candidate:10 1 UDP 335544831 10.10.36.134 50589 typ relay raddr 172.19.0.2 rport 9', remote: 'candidate:3993072200 1 UDP 41885695 10.10.36.134 53894 typ relay', stream_id: '1', component_id: 1
...
New candidate pair selected, local: 'candidate:7 1 UDP 1677722111 10.10.36.131 46842 typ srflx raddr 172.19.0.2 rport 46842', remote: 'candidate:266015763 1 UDP 2122260223 10.10.1.57 55125 typ host', stream_id: '1', component_id: 1
kurento_logs_webrtc_NOT_working.txt
new candidate pair selected, local: 'candidate:10 1 UDP 335544831 10.10.36.134 51280 typ relay raddr 172.19.0.2 rport 9', remote: 'candidate:3993072200 1 UDP 41885695 10.10.36.134 51287 typ relay', stream_id: '1', component_id: 1
Сначала я подумал, что вы повторно используете старых кандидатов, но порты изменились. Смена браузеров может изменить число кандидатов, я не ожидал, что они будут детерминированными между прогонами, поэтому мне пришлось посмотреть дважды.
Есть одно небольшое отличие от журнала - это нерабочий IceComponentStateChanged
изменения в connecting
после candidate:266015763
появляется скорее, чем раньше. Я не знаю, если это важно.
Главные примечания:
В прошлом, когда у нас было несколько категорий проблем:
- на стороне клиента мы теряли кандидатов ICE - некоторые из кандидатов были отправлены до того, как мы были готовы, поэтому нам нужно было поставить их в очередь. IIRC, возможно, есть некоторые кандидаты в предложении SDP (или ответ, извините, это было давно), поэтому ваши слушатели должны быть готовы, прежде чем начать.
- мы отправляли старых кандидатов - на сервере сигнализации использовался неправильный элемент воспроизведения, чтобы предложить кандидатов клиентам, и они больше не действительны.
Я бы порекомендовал вам использовать Chrome с http://chrome//webrtc-internals, чтобы помочь. Проблемы кандидатов в ICE видны в webrtc-internals, поскольку вы можете видеть конечный автомат, проходящий через его состояние. В нашем рабочем случае гораздо больше переходов, чем в сломанных.
Добавление слушателей на стороне клиента для трех ледовых событий также полезно:
this.peer.peerConnection.oniceconnectionstatechange = this.logloglog.bind(this, this.peer.peerConnection);
this.peer.peerConnection.onicegatheringstatechange = this.logloglog.bind(this, this.peer.peerConnection);
this.peer.peerConnection.onsignalingstatechange = this.logloglog.bind(this, this.peer.peerConnection);
Это позволяет увидеть, как идут переговоры, но в основном это то, что находится в http://chrome//webrtc-internals.
последнее замечание, это то, что я использовал в разделе журнала /etc/default/kurento-media-server
:
# ICE debug logging: uncomment to enable in KMS and in the 3rd-party library 'libnice'
# - Note: This can get very verbose, log size will explode in the long term
#export GST_DEBUG="$GST_DEBUG,kmsiceniceagent:5,kmswebrtcsession:5,webrtcendpoint:4"
export G_MESSAGES_DEBUG="libnice,libnice-stun"
export NICE_DEBUG="$G_MESSAGES_DEBUG"
Я не помню, были ли они лучше, чем вы использовали, но я добавлю это там.