Служебные сообщения

Ответ на RPC запрос

Ответ на RPC-запрос обычно оборачивается следующим образом:

rpc_result#f35c6d01
    req_msg_id:long
    result:Object
= RpcResult;

Здесь req_msg_id является идентификатором сообщения, отправленного другой стороной, и содержащего RPC запрос. Таким образом, получатель понимает, что result является ответом на конкретный рассматриваемый RPC запрос.

Заодно этот ответ служит подтверждением принятия другой стороной сообщения req_msg_id.

Обратите внимание, что ответ на RPC запрос должен также быть подтверждён. Чаще всего, это совпадает с передачей следующего сообщения (к которому может быть приделан контейнер, содержащий в том числе служебное сообщение с подтверждением).

RPC-ошибка

На любой RPC-запрос может также вернуться сообщение об ошибке в качестве поля result, оформленное следующим образом:

rpc_error#2144ca19
    error_code:int
    error_message:string
= RpcError;

Отмена RPC-запроса

В определённых ситуациях клиент не хочет принимать ответ на уже переданный RPC запрос, например потому, что ответ оказывается длинным и клиент решил обойтись без него из-за недостаточной ёмкости канала. Обычное прерывание TCP-соединения не даст эффекта, потому что сервер отправит заново недостающий ответ при первой возможности. Поэтому клиенту нужен способ отменить получение ответного сообщения RPC, фактически подтверждая его принятие до того, как оно на самом деле будет принято — в этом случае сервер успокоится и не будет больше его слать. Однако клиент не знает msg_id RPC-ответа до принятия ответа; единственное, что он знает, — это req_msg_id. т. е. msg_id релевантного RPC запроса. Поэтому используется специальный запрос:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Ответ на этот запрос возвращается как одно из следующих сообщений, упакованное в rpc_result и требующее подтверждения:

rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7
    msg_id:long 
    seq_no:int 
    bytes:int
= RpcDropAnswer;

Первая версия ответа используется, если сервер ничего не помнит о присланном req_msg_id (например, если на него уже был отправлен ответ). Вторая версия используется, если ответ был отменён во время того, как обрабатывался RPC запрос (в случае если сам RPC-запрос всё ещё обрабатывался полностью); в этом случае такой же rpc_answer_dropped_running так же возвращается в ответ на оригинальный запрос, и оба этих ответа требуют подтверждения от клиента. Последняя версия означает, что RPC-ответ был удалён из исходящей очереди сервера, и его msg_id, seq_no и длина в байтах передаются клиенту.

Обратите внимание, что rpc_answer_dropped_running и rpc_answer_dropped служат как подтверждения принятия сервером оригинального запроса (тот же, ответ на который мы хотим забыть). Вдобавок, также как для любого RPC-запроса, любой ответ на rpc_drop_answer является подтверждением самого rpc_drop_answer.

Как альтернатива к использованию rpc_drop_answer, может быть создана новая сессия — после сброса соединения и удаления старой сессии через destroy_session.

Сообщения, связанные с запросом, изменением и получением состояния других сообщений

См. «служебные сообщения о сообщениях»

Запрос нескольких будущих солей

Клиент может в любое время запросить у сервера несколько (от 1 до 64) будущих солей сервера вместе со сроками их действия. Сохраняя их в постоянной памяти, клиент может использовать их, чтобы в будущем отправлять сообщения, даже если он меняет сессии (соль сервера привязана к ключу авторизации, а не к конкретной сессии).

get_future_salts#b921bd04 num:int = FutureSalts;
future_salt#0949d9dc
    valid_since:int
    valid_until:int 
    salt:long
= FutureSalt;
future_salts#ae500895
    req_msg_id:long
    now:int
    salts:Vector<future_salt>
= FutureSalts;

Клиент должен проверить, что req_msg_id ответа фактически совпадает с msg_id запроса для get_future_salts. Сервер возвращает максимум num будущих солей сервера (может вернуть меньше). Ответ служит подтверждением запроса и сам не требует подтверждения.

Пинг сообщений (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Ответ обычно возвращается в том же соединении:

pong#347773c5 msg_id:long ping_id:long = Pong;

Эти сообщения не требуют подтверждений. Понг (pong) передаётся только в ответ на пинг, в то время как пинг может быть инициирован любой стороной.

Отложенное закрытие соединения + PING

ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;

Работает как пинг. Кроме того, после того как оно получено, сервер включает таймер, который закроет текущее соединение disconnect_delay seconds позже, если оно не получит новое сообщение того же типа, которое автоматически устанавливает заново все предыдущие таймеры. Если клиент отправляет эти пинги по штуке каждые 60 секунд, например, он может установить disconnect_delay равным 75 секундам.

Запрос на&nbsp;уничтожение сессии

Используется клиентом, чтобы уведомить сервер, что он может забыть данные другой сессии, принадлежащей тому же юзеру. (т. е. с тем же auth_key_id). Результат применения этого к текущей сессии является неопределённым (undefined )

destroy_session_ok#e22045fc session_id:long = DestroySessionRes
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;

Уведомление о создании новой сессии

Сервер уведомляет клиент что должна быть создана новая (с точки зрения сервера) сессия для обработки сообщения клиента. Если после этого сервер получает сообщение с с меньшим msg_id внутри той же сессии, сходное уведомление будет также сгенерировано для этого msg_id. Такие уведомления не создаются для высоких/больших значений msg_id.

new_session_created#9ec20908
    first_msg_id:long
    unique_id:long
    server_salt:long
= NewSession;

Параметр unique_id генерируется сервером каждый раз, когда (пере)создаётся сессия.

Это уведомление должно быть подтверждено клиентом. Это нужно, например, для того, чтобы клиент понял что фактически существует разрыв (gap) в потоке очереди ожидающих уведомлений, полученных от сервера (на протяжении какого-то периода времени у юзера может не получится принять уведомления).

Обратите внимание, что сервер может в одностороннем порядке уничтожить (закрыть) любую из существующих сессий клиента со всеми ожидаемыми сообщениями и уведомлениями, не присылая никаких уведомлений. Это случается, например, если сессия долгое время неактивна, и у сервера заканчивается память. Если клиент в какой-то момент решает отправить новые сообщения серверу, используя старую сессию, уже забытую сервером, будет сгенерировано уведомление о «создании новой сессии». От клиента ожидается успешная обработка таких ситуаций.

Контейнеры

Контейнеры — это сообщения, которые содержат несколько других сообщений. Используются для возможности передавать несколько RPC-запросов и/или служебных сообщений одновременно, с использованием протокола HTTP или даже TCP или UDP. Контейнер может быть принят или отклонён другой стороной только целиком. Подтверждение для контейнера автоматически служит подтверждением для всех входящих в него сообщений.

Простой контейнер

Предупреждение

Простые контейнеры не используются в текущей TL-Schema, хотя и указаны в ней в виде комментариев.

Простой контейнер заключает в себе несколько сообщений следующим образом:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Здесь сообщение ссылается на любое сообщение вместе с его длиной и msg_id:

message 
    msg_id:long
    seqno:int
    bytes:int
    body:Object
= Message;

msg_id — это число байт в сериализации тела.

Все сообщения в контейнере должны иметь msg_id ниже чем сам контейнер. Контейнер не требует подтверждения и может не содержать другие простые контейнеры. Когда сообщения отправляются повторно, они могут быть скомбинированы в контейнер другим способом или быть отправлены отдельно.

Пустые контейнеры также допускаются. Они используются сервером, например, чтобы ответить на запрос HTTP, когда таймаут, указанный в http_wait заканчивается, и нет сообщений для передачи.

Копии сообщений

Предупреждение

Копии сообщений не используются в текущей TL-Schema, хотя и указаны в ней в виде комментариев.

В некоторых ситуациях, старое сообщение с недействительным более msg_id нужно отправить заново. Тогда оно упаковывается в копию контейнера:

msg_copy#e06046b2 orig_message:Message = MessageCopy;

Однажды полученное сообщение обрабатывается так, как если бы упаковщик (wrapper) не существовал бы. Однако, если доподлинно известно, что orig_message.msg_id сообщения был получен, то новое сообщение не обрабатывается (в то же время, оно и orig_message.msg_id подтверждаются). Значение orig_message.msg_id должно быть ниже, чем msg_id контейнера.

В настоящее время это не используется, потому что старое сообщение может быть упаковано в простой контейнер с тем же результатом.

Упакованный объект

Предупреждение

Упакованные контейнеры не используются в текущей TL-Schema, хотя и указаны в ней в виде комментариев.

Используется для замены любого другого объекта (или точнее, их сериализации) с его заархивированным (gzipped) представлением:

gzip_packed#3072cfa1 packed_data:string = Object;

В настоящее время, оно поддерживается в теле RPC-ответа (т. е. как результат в rpc_result) и генерируется сервером для ограниченного числа запросов высокого уровня. Также в будущем оно может быть использовано для передачи не-служебных сообщений (т. е. RPC-запросов) от клиента к серверу.

HTTP Wait/Long Poll (очередь ожидания)

Для обеспечения возможности отправки сервером сообщений клиенту через HTTP-протокол используется следующий специальный служебный запрос, не нуждающийся в подтверждении (который должен быть отправлен именно через HTTP-соединение):

http_wait#9299359f
    max_delay:int
    wait_after:int
    max_wait:int
= HttpWait;

При получении такого сообщения (или контейнера, содержащего такое сообщение) сервер либо ждет max_delay миллисекунд, после чего отправляет все сообщения, что у него накопились для клиента, если есть хотя бы одно (поместив их при необходимости в контейнер, в который также могут быть добавлены подтверждения), либо ждет не более max_wait миллисекунд, пока такое сообщение не появится. Если сообщений так и не появилось, отправляется пустой контейнер.

Параметр max_delay обозначает максимальное количество миллисекунд, которое проходит между первым сообщением для этой сессии и передачей HTTP-ответа. Параметр wait_after работает следующим образом: после принятия последнего сообщения из отдельной сессии, сервер ждёт ещё wait_after миллисекунд в случае, если есть ещё сообщения. Если дополнительных сообщений нет, передаётся результат (контейнер со всеми сообщениями). Если появляется другие / больше сообщений, счётчик wait_after устанавливается заново.

В то же время, параметр max_delay имеет более высокий приоритет, чем wait_after, а max_wait имеет более высокий приоритет, чем max_delay.

Сообщение не требует ответа или подтверждения. Если контейнер, переданный через HTTP , содержит несколько таких сообщений, его поведение не определено (в действительности, будет активирован последний параметр).

По умолчанию, min_wait=0 (мс), wait_after=0 (мс) и max_wait=25000 (мс).

Если у клиента большой пинг до сервера, вероятно, имеет смысл установить min_wait в значение, сопоставимое с пингом по порядку величины.