Содержание

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

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

Ответ на RPC запрос обычно упаковывается так:

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

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

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

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

Ошибка RPC

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

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.

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

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

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

Клиент может в любое время запросить у сервера несколько (от 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 секундам.

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

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

destroy_session#e7512126
    session_id:long = DestroySessionRes;
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. Контейнер может быть принят или отклонён другой стороной только целиком. Подтверждение для контейнера автоматически служит подтверждением для всех входящих в него сообщений.

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

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

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 заканчивается, и нет сообщений для передачи.

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

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

msg_copy#e06046b2
    orig_message:Message = MessageCopy;

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

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

Упакованный объект (Packed Object)

Используется для замены любого другого объекта (или точнее, их сериализации) с его заархивированным (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 , содержит несколько таких сообщений, его поведение не определено (undefined) (в действительности, будет активирован последний параметр).

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

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

Комментарии