Кратко

  • Корректный CONNECTION_CLOSE немедленно завершает соединение QUIC и неявно закрывает открытые потоки.
  • Отправитель переходит в closing, получатель — в draining; правила ответа у состояний различаются.
  • Код и текст причины описывают транспортное свидетельство, а не завершение бизнеса, надёжную фиксацию или причину.

Защищённый CONNECTION_CLOSE — аутентифицированный сигнал транспорта от узла-партнёра. Он показывает, что принимающий узел увидел корректный фрейм и что соединение вошло в соответствующий путь завершения. Но это не превращает сетевое событие в квитанцию приложения. Особенно опасна система, которая видит NO_ERROR и помечает все незавершённые процессы на соединении как успешные.

QUIC немедленно прекращает транспорт. Открытые потоки сразу закрываются и могут рассматриваться как неявно сброшенные. Узел, отправивший закрытие, входит в closing, а принявший — в draining. Это надёжно описывает состояние соединения, но не доказывает согласованное завершение на уровне приложения, получение и обработку каждого сообщения, надёжную фиксацию состояния, компенсацию незавершённых действий или завершение бизнес-процесса.

В журнале должна сохраняться разновидность фрейма. Тип 0x1c несёт ошибки уровня QUIC, включая NO_ERROR, и содержит поле типа вызвавшего фрейма; если тип неизвестен, значение поля равно нулю. Тип 0x1d несёт коды ошибок протокола приложения и не содержит этого поля. Транспортный код не является подтверждением приложения. NO_ERROR означает только использование транспортного кода без ошибки. Текст причины — необязательный диагностический текст, предоставленный партнёром; он может быть пустым и не имеет языковой метки. Сам по себе он не является полным анализом причины.

Состояния closing и draining нужны для корректного отбрасывания задержанных или переупорядоченных пакетов. Обычно они должны сохраняться не менее трёх текущих интервалов PTO. Документированный альтернативный контроль, не позволяющий поздним пакетам вызвать ответ, может допустить более раннее удаление состояния. После выхода из любого состояния состояние соединения отбрасывается, а последующий пакет может получить Stateless Reset. Поэтому момент удаления нужно фиксировать отдельно от момента завершения приложения.

Состояния асимметричны. В closing узел сохраняет только сведения, необходимые для распознавания пакетов соединения и формирования ответов CONNECTION_CLOSE. Обрабатывать полученные фреймы он не обязан; ответы следует ограничивать, а при невозможности проверить входящие пакеты нужно соблюдать ограничения усиления. В draining отправка пакетов запрещена. До входа в это состояние можно отправить не более одного пакета закрытия, затем узел молчит. Такое молчание не доказывает успех бизнеса.

Важен и уровень защиты. После подтверждения рукопожатия CONNECTION_CLOSE должен отправляться в пакете 1-RTT. До подтверждения может потребоваться отправка на нескольких доступных уровнях, чтобы партнёр смог обработать хотя бы одну копию. Клиент не вправе считать, что сервер принял закрытие, отправленное только в 0-RTT. Отсутствие видимого закрытия также не доказывает автоматически, что партнёр его проигнорировал.

Доказательства приложения должны собираться отдельно: согласование завершения, принятие каждой операции, надёжная фиксация, компенсация и завершение. Для установления причины нужны свидетельства, независимые от фрейма закрытия. Текст причины может помочь расследованию, но не заменяет эти записи.