Получение практически всех сообщений (за исключением некоторых чисто служебных, а также незашифрованных сообщений, используемых впротоколе создания авторизационного ключа) должно быть подтверждено. Для этого используется следующее служебное сообщение (не требующее подтверждения):
msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;
Сервер обычно подтверждает получение сообщения от клиента (как правило — RPC-запроса) с помощью RPC-ответа. Если же ответа долго нет, сервер может сначала отправить подтверждение получения, а через какое-то время — сам RPC-ответ.
Клиент обычно подтверждает получение сообщения от сервера (обычно — RPC-ответа), добавляя подтверждение в следующий RPC-запрос, если он отправляется не слишком поздно (скажем, если он возникает в течение 60-120 секунд после получения сообщения от сервера). Однако если долго нет повода послать сообщения к серверу, или если неподтвержденных сообщений от сервера становится много (скажем, больше 16), то клиент отправляет подтверждение само по себе.
В некоторых случаях сервер может уведомить клиента, что присланное им сообщение было проигнорировано по той или иной причине. Отметим, что такое уведомление не может быть сгенерировано, если сообщение не было корректно расшифровано сервером.
bad_msg_notification#a7eff811
bad_msg_id:long
bad_msg_seqno:int
error_code:int
= BadMsgNotification;
bad_server_salt#edab447b
bad_msg_id:long
bad_msg_seqno:int
error_code:int
new_server_salt:long
= BadMsgNotification;
Здесь error_code
может принимать в том числе следующие значения:
msg_id
(скорее всего, на клиенте неправильное время; имеет смысл синхронизировать его, использовав msg_id
уведомления, и перепослать исходное сообщение с «правильным» msg_id, либо обернуть его в контейнер с новым msg_id, если исходное сообщение слишком долго ждало отправки на клиенте)msg_id
(аналогично предыдущему случаю: надо синхронизировать время на клиенте и перепослать сообщение с правильным msg_id
)msg_id
(сервер ожидает, что msg_id
клиентских сообщений делится на 4)msg_id
контейнера совпал с msg_id
ранее полученного сообщения (такого никогда не должно быть)msg_id
или нетmsg_seqno
(сервером уже было принято сообщение с меньшим msg_id
, но с большим seqno, либо с таким же нечетным)msg_seqno
(аналогично: есть сообщение с большим msg_id
, но с меньшим seqno, либо с таким же нечетным)msg_seqno
(несущественное сообщение), получили нечетныйmsg_seqno
(существенное сообщение), получили четныйbad_server_salt
с правильной солью, надо перепослать сообщение с ней)По замыслу, значения error_code
группируются по (error_code
4): например, коды 0x40..0x4f
соответствуют ошибкам при разборе контейнера.
Уведомления о проигнорированном сообщении не нуждаются в подтверждении (т.е. являются несущественными).
Важно: если на сервере изменился server_salt
, или если у клиента неправильное время, на любой запрос будет получено уведомление указанного выше вида. Клиент должен проверить, что он действительно недавно отправлял сообщение с указанным msg_id
, и если это так — обновить у себя поправку времени (разницу между серверными и клиентскими часами) и серверную соль, исходя из msg_id
и server_salt
уведомления, чтобы использовать их для (пере)отправки будущих сообщений. При этом исходное сообщение (то, на которое вернули уведомление об ошибке) тоже должно быть перепослано с более адекватным msg_id
и/или server_salt
.
Кроме того, клиент может обновлять значение server_salt
, используемое при отправке сообщений на сервер, исходя из значений в rpc-ответах или контейнерах, содержащих rpc-ответ, при условии, что этот rpc-ответ действительно соответствует недавно отправленному запросу. (В случае сомнения лучше не обновлять, т.к. есть риск replay-атаки.)
Если одна из сторон долго не получает информации о состоянии отправленных ей сообщений, она может явно запросить ее у противоположной стороны:
msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
Ответ на этот запрос содержит в себе следующую информацию:
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
Здесь req_msg_id
— идентификатор запроса msgs_state_req
, info
— строка, которая для каждого сообщения из присланного списка msg_ids
содержит ровно один байт с состоянием сообщения:
msg_id
слишком мал, противоположная сторона могла его забыть)msg_id
в диапазоне хранимых идентификаторов, однако противоположная сторона такого сообщения точно не получала)msg_id
слишком велик, противоположная сторона его точно еще не получила)Этот ответ не нуждается в подтверждении. Сам по себе он является подтверждением на соответствующий msgs_state_req
.
Отметим, что если вдруг выясняется, что у противоположной стороны нет сообщения, которое вроде бы было ей отправлено, можно просто отправить это сообщение снова. Даже если на противоположную сторону вдруг придет сразу два экземпляра этого сообщения, дубль будет проигнорирован. (Если прошло слишком много времени и исходный msg_id
уже не годится, надо обернуть сообщение в msg_copy
.)
Любая сторона может добровольно информировать другую сторону о состоянии сообщений, посланных другой стороной.
msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
Перечисляются все коды сообщений, известных данной стороне, за исключением тех, для которых установлен флаг +128
или +16
. Однако если установлен флаг +32
, но не +64
, то состояние сообщения все-таки будет прислано.
Такое сообщение не нуждается в подтверждении.
Обычно используется сервером для ответа на повторное получение сообщения msg_id
, особенно в том случае, если на это сообщение был сгенерирован ответ, и этот ответ большой. Если ответ небольшой, сервер может вместо этого повторить сам ответ.
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
Второй вариант используется для уведомления о сообщениях, которые были созданы на сервере не в результате rpc-запроса (например, уведомления о новых сообщениях), были отосланы клиенту какое-то время назад, но на них не было получено подтверждения.
Такое сообщение не нуждается в подтверждении.
msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
В качестве ответа удаленная сторона немедленно перепосылает запрошенные сообщения, обычно по тому же соединению, по которому пришел этот запрос. Если сообщение с запрошенным msg_id
отсутствует или уже было забыто, либо если оно было отправлено запрашивающей стороной (что понятно по четности), для таких msg_id
присылается ответ MsgsStateInfo
.