Перейти к основному содержимому
Версия: 5.x

Протокол

На этой странице описан формат передачи данных — для отладки и для написания клиента на другом языке. Официальные клиенты реализуют его полностью.

Транспорт​

  • WebSocket upgrade на путь, переданный в MapDarkWs. Браузеры отправляют заголовок Origin, который сервер может проверять по AllowedOrigins.
  • Необязательный query-параметр (по умолчанию token) передаётся в аутентификатор сервера.
  • Только текстовые фреймы. Сейчас сервер обрабатывает бинарный фрейм как текст с теми же байтами, а .NET-клиент закрывает сокет с кодом 1003; не полагайтесь на бинарные фреймы.
  • Сообщения больше серверного MaxMessageSizeBytes (1 MiB) закрывают соединение с кодом 1009.

JSON-сообщения​

НаправлениеФормат
Запрос{ "id": string, "action": string, "data"?: unknown }
Успех{ "id": string, "data"?: unknown }
Ошибка{ "id": string, "error": string, "data"?: unknown }
Рассылка{ "id": "@", "action": string, "data"?: unknown }
→ { "id": "7f3c", "action": "math:sum", "data": { "left": 2, "right": 3 } }
← { "id": "7f3c", "data": { "value": 5 } }

→ { "id": "7f3d", "action": "message:delete", "data": { "id": "42" } }
← { "id": "7f3d", "error": "message:forbidden" }

← { "id": "@", "action": "message:created", "data": { "text": "hi" } }
  • Идентификаторы запросов выбирает клиент. Они должны быть непустыми и не могут быть @ или @auth; запрос с зарезервированным id отклоняется, а его действие не выполняется.
  • Ответы несут id запроса и могут приходить в любом порядке.
  • Ответ считается ошибкой всякий раз, когда в нём есть поле error.
  • data опускается только тогда, когда сервер использовал перегрузку без данных (Ok(), PublishAsync(target, action)). Перегрузки с данными всегда записывают их, а для значения null — как null.
  • Имена полей конверта зафиксированы атрибутами JsonPropertyName: id, action, data и error. Политики именования JsonOptions применяются к свойствам прикладных DTO внутри data (по умолчанию camelCase), а не к именам конверта.
  • Ответ invalid-request на запрос с зарезервированным id использует пустой id, чтобы его нельзя было спутать с рассылкой или устаревшим управляющим ответом.

Текстовые команды​

Системные команды и ответы на них передаются обычным текстом, без JSON и id:

КомандаУспехОшибка
auth:<token>auth:successauth:failed
logoutlogout:successСоединение обрывается, если logout не удаётся завершить
pingpongНет

Поскольку у ответов нет id, клиент должен отправлять не более одной команды auth:/logout за раз и дожидаться ответа на неё. Если ответ не пришёл вовремя, клиенту следует закрыть сокет, чтобы запоздавший ответ не был принят за ответ на следующую команду. Команды обрабатываются по порядку вместе с запросами в том же соединении.

Регулярно отправляйте ping: сервер на .NET 8 закрывает соединения, молчащие дольше ReceiveIdleTimeout (2 минуты). Отсутствие pong сообщает клиенту, что соединение мертво.

Коды ошибок​

КодЗначение
darkws:error:invalid-actionТакого действия нет
darkws:error:invalid-requestНекорректный JSON, отсутствующие или неверные поля либо payload, который не удаётся привязать
darkws:error:authorization-requiredДействию нужна сессия
darkws:error:request-failedОбработчик неожиданно завершился с ошибкой
darkws:error:busyОчередь запросов соединения оставалась заполненной
auth:failedТекстовый ответ на отклонённую команду auth:

Серверы могут переименовать коды darkws:error:* через DarkWsOptions. Все остальные коды приходят из приложения.

Коды закрытия и HTTP-статусы​

КодКто отправляетПричина
HTTP 400СерверЭто не WebSocket-запрос
HTTP 401СерверАутентификатор при upgrade выбросил исключение
HTTP 403СерверOrigin не разрешён
1000Любая сторонаНормальное закрытие
1003.NET-клиентСервер отправил бинарный фрейм
1008Серверauth:/logout пришла, когда очередь команд была заполнена
1009Любая сторонаСообщение превысило лимит размера

Контракт жизненного цикла клиентов​

Оба клиента говорят на одном протоколе, но придерживаются разных политик жизненного цикла:

СлучайБраузерный DarkWs.NET DarkWsClient
Сокет обрывается при отключённом переподключенииОжидающие вызовы завершаются ошибкой; фоновых повторов нет. Следующий запрос или отправка открывает новый сокет.Ожидающие вызовы завершаются ошибкой. Последующие вызовы завершаются ошибкой до явного ConnectAsync.
Автоматическая аутентификация получает auth:failedВызовы в очереди отклоняются с ErrorResponse. Срабатывает sessionRestoreFailed, затем open сигнализирует о готовности без сессии; последующие вызовы уходят анонимно.Готовность завершается ошибкой DarkWsResponseException. Состояние становится Disconnected с указанием причины, и повторы прекращаются до ConnectAsync.
Провайдер токена выбрасывает исключениеТо же событие об ошибке и анонимный откат, если сокет ещё открыт.Постоянная ошибка готовности до ConnectAsync.
Провайдер токена не возвращает токенПодключается анонимно, без sessionRestoreFailed.Постоянная ошибка готовности.

В браузере connected и open отражают состояние транспорта и не доказывают, что аутентификация прошла успешно. В .NET Connected наступает после успешного восстановления. LazyDarkWs следует браузерной политике; его close() дополнительно позволяет переподключиться при следующем использовании.

Ни один из клиентов не хранит токены, переданные для ручной аутентификации. Переподключения используют только query приложения или провайдер токена.