114 lines
5.3 KiB
Markdown
114 lines
5.3 KiB
Markdown
# Логика установки соединения через сервер
|
||
|
||
Ниже описан фактический flow звонка в текущей реализации SHiNE с опорой на сообщения, которые проходят через сервер.
|
||
|
||
## 1) Основные операции и типы сообщений
|
||
|
||
Клиентские WS-операции:
|
||
- `CallInviteBroadcast` — широковещательный входящий вызов пользователю.
|
||
- `CallSignalToSession` — точечный сигнал в конкретную сессию.
|
||
|
||
Серверные события клиенту:
|
||
- `IncomingCallInvite` — уведомление о входящем вызове.
|
||
- `IncomingCallSignal` — сигнал по активному `callId`.
|
||
|
||
Коды `type` в `IncomingCallSignal`:
|
||
- `100` — `INVITE`
|
||
- `110` — `RINGING`
|
||
- `120` — `ACCEPT`
|
||
- `130` — `DECLINE_BUSY`
|
||
- `140` — `TIMEOUT`
|
||
- `150` — `HANGUP`
|
||
- `200` — `OFFER`
|
||
- `210` — `ANSWER`
|
||
- `220` — `ICE`
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
Именно комбинация этих трёх пунктов закрывает основные причины «иногда дозванивается, иногда нет» при мультидевайсе.
|