SHiNE-server/Логика_установки_соединения_через_сервер_СТАРЫЙ_УДАЛИТЬ.md

5.3 KiB
Raw Blame History

Логика установки соединения через сервер

Ниже описан фактический flow звонка в текущей реализации SHiNE с опорой на сообщения, которые проходят через сервер.

1) Основные операции и типы сообщений

Клиентские WS-операции:

  • CallInviteBroadcast — широковещательный входящий вызов пользователю.
  • CallSignalToSession — точечный сигнал в конкретную сессию.

Серверные события клиенту:

  • IncomingCallInvite — уведомление о входящем вызове.
  • IncomingCallSignal — сигнал по активному callId.

Коды type в IncomingCallSignal:

  • 100INVITE
  • 110RINGING
  • 120ACCEPT
  • 130DECLINE_BUSY
  • 140TIMEOUT
  • 150HANGUP
  • 200OFFER
  • 210ANSWER
  • 220ICE

2) Старт исходящего звонка

  1. Инициатор создаёт callId и отправляет:
    • CallInviteBroadcast(toLogin, callId, type=100).
  2. Сервер находит все активные WS-сессии целевого логина и шлёт им IncomingCallInvite.
  3. На устройствах callee появляется экран входящего вызова.

3) Ранние статусы до поднятия трубки

Каждое устройство callee может отправить инициатору:

  • RINGING (110) — «звонок идёт».

Для исходящего звонка инициатор фиксирует выбранную remoteSessionId:

  • первый валидный RINGING/ACCEPT выбирает сессию,
  • сигналы с тем же callId, но от других сессий этого же пользователя, игнорируются.

Это защищает от гонок мультидевайса.


4) Принятие звонка на одном устройстве callee

  1. На выбранном устройстве callee пользователь нажимает «Поднять»:
    • отправляется ACCEPT (120) в выбранную сессию инициатора.
  2. Сервер дополнительно рассылает на другие сессии этого же callee:
    • HANGUP (150) с data=accepted_on_other_device.
  3. Остальные устройства callee закрывают экран входящего вызова.

Итог: активным остаётся один путь «инициатор ↔ выбранная сессия callee».


5) Обмен SDP (OFFER/ANSWER)

После ACCEPT:

  1. Инициатор формирует RTCPeerConnection, createOffer(), отправляет:
    • OFFER (200).
  2. Calee применяет setRemoteDescription(offer), делает createAnswer(), отправляет:
    • ANSWER (210).
  3. Инициатор применяет setRemoteDescription(answer).

Защиты:

  • повторный ACCEPT/повторный старт offer игнорируется;
  • ANSWER обрабатывается только для исходящего звонка;
  • ANSWER без локального offer или дубликат в stable игнорируется.

6) Обмен ICE-кандидатами

ICE (220) может приходить раньше SDP. Поэтому:

  • если pc ещё не создан — ICE кладётся в очередь;
  • если pc есть, но remoteDescription ещё нет — ICE тоже в очередь;
  • после установки remoteDescription очередь применяется (addIceCandidate).

Это устраняет race «remote description was null».


7) Завершение и ошибки

Нормальное завершение:

  • HANGUP (150) от одной стороны → вторая завершает звонок.

Неуспех установки:

  • сторона, у которой setup не удался, шлёт HANGUP (150) с data=setup_failed:...,
  • вторая сторона сразу закрывает экран ожидания.

Также пишутся CallDeliveryReport:

  • call_connected, incoming_failed, outgoing_failed, unknown_error и расширенная диагностика ICE/SDP.

8) Почему схема устойчива сейчас

Текущая устойчивость обеспечивается тремя правилами:

  1. Одна выбранная сессия callee для исходящего звонка.
  2. Принятие на одном устройстве закрывает входящий на остальных через сервер.
  3. ICE буферизуется до готовности SDP/PC, а не ломает handshake.

Именно комбинация этих трёх пунктов закрывает основные причины «иногда дозванивается, иногда нет» при мультидевайсе.