Ответ на RPC-запрос обычно оборачивается следующим образом:
rpc_result#f35c6d01
req_msg_id:long
result:Object
= RpcResult;
Здесь req_msg_id
является идентификатором сообщения, отправленного другой стороной, и содержащего RPC запрос. Таким образом, получатель понимает, что result
является ответом на конкретный рассматриваемый RPC запрос.
Заодно этот ответ служит подтверждением принятия другой стороной сообщения req_msg_id
.
Обратите внимание, что ответ на RPC запрос должен также быть подтверждён. Чаще всего, это совпадает с передачей следующего сообщения (к которому может быть приделан контейнер, содержащий в том числе служебное сообщение с подтверждением).
На любой RPC-запрос может также вернуться сообщение об ошибке в качестве поля result
, оформленное следующим образом:
rpc_error#2144ca19
error_code:int
error_message:string
= RpcError;
В определённых ситуациях клиент не хочет принимать ответ на уже переданный 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#7abe77ec ping_id:long = Pong;
Ответ обычно возвращается в том же соединении:
pong#347773c5 msg_id:long ping_id:long = Pong;
Эти сообщения не требуют подтверждений. Понг (pong) передаётся только в ответ на пинг, в то время как пинг может быть инициирован любой стороной.
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
Работает как пинг. Кроме того, после того как оно получено, сервер включает таймер, который закроет текущее соединение disconnect_delay
seconds позже, если оно не получит новое сообщение того же типа, которое автоматически устанавливает заново все предыдущие таймеры. Если клиент отправляет эти пинги по штуке каждые 60 секунд, например, он может установить disconnect_delay равным 75 секундам.
Используется клиентом, чтобы уведомить сервер, что он может забыть данные другой сессии, принадлежащей тому же юзеру. (т. е. с тем же 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-протокол используется следующий специальный служебный запрос, не нуждающийся в подтверждении (который должен быть отправлен именно через 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
в значение, сопоставимое с пингом по порядку величины.