Параметры конфигурации QUIC

Библиотека System.Net.Quic использует классы параметров для настройки объектов протокола (QuicListener и QuicConnection) перед их построением и инициализацией. Существует три различных класса опций для этого:

Все классы параметров можно настроить постепенно, что означает, что они не требуют инициализации любого из их свойств с помощью конструктора и могут быть настроены независимо. Но когда они используются для настройки нового прослушивателя или подключения, параметры проверяются, а соответствующий тип ArgumentException вызывается для любых отсутствующих обязательных значений или неправильно настроенных. Например, если обязательный QuicConnectionOptions.DefaultStreamErrorCode параметр не задан, вызов ConnectAsync(QuicClientConnectionOptions, CancellationToken) вызывает вызов ArgumentOutOfRangeException.

QuicListenerOptions

QuicListenerOptions используется в QuicListener.ListenAsync(QuicListenerOptions, CancellationToken) при запуске нового QuicListener. Индивидуальные свойства конфигурации:

Протоколы приложений

ApplicationProtocols определите протоколы приложений, принятые сервером (RFC 7301 — ALPN). Он может содержать несколько значений для разных протоколов, которые могут быть не связаны. В процессе принятия нового подключения прослушиватель может сузить или выбрать один конкретный протокол для каждого входящего подключения, см. в разделе QuicListenerOptions.ConnectionOptionsCallback. Это свойство является обязательным и должно содержать по крайней мере одно значение.

ПараметрыПодключенияCallback

ConnectionOptionsCallback — делегат для выбора QuicServerConnectionOptions для входящего подключения. Функция получает частично инициализированный экземпляр QuicConnection и SslClientHelloInfo содержит имя сервера, запрошенное клиентом (RFC 6066 — SNI). Делегат вызывается для каждого входящего подключения. Он может возвращать различные параметры на основе предоставленных сведений клиента или безопасно возвращать один и тот же экземпляр параметров каждый раз. Назначение делегата и форма намеренно похожи на ServerOptionsSelectionCallback, используемый в SslStream.AuthenticateAsServerAsync(ServerOptionsSelectionCallback, Object, CancellationToken). Это свойство является обязательным.

ПрослушивательBacklog

ListenBacklog определяет, сколько входящих подключений может удерживаться прослушивателем, прежде чем дополнительные начинают отклоняться. Каждая попытка установить подключение имеет значение, даже если она завершается сбоем или если подключение прерывается во время ожидания в очереди. Текущие процессы по установлению новых подключений также учитываются в этом лимите. Подключения или попытки подключения учитываются до тех пор, пока они не будут получены с помощью QuicListener.AcceptConnectionAsync(CancellationToken). Цель ограничения невыполненной работы заключается в том, чтобы предотвратить перегрузку серверов более входящими подключениями, чем они могут обрабатывать. Это свойство является необязательным, значение по умолчанию — 512.

Точка прослушивания

ListenEndPoint содержит IP-адрес и порт, на котором прослушиватель будет принимать новые подключения. Из-за базовой реализации MsQuic, этот прослушиватель всегда привязывается к двухстековому шаблонному сокету независимо от того, что указано здесь. Это может привести к непредвиденному поведению, особенно в сравнении с обычными сокетами TCP, например в случаях HTTP/1.1 и HTTP/2. Дополнительные сведения см. в руководстве по устранению неполадок QUIC. Это свойство является обязательным.

QuicConnectionOptions

QuicConnectionOptions Параметры разделяются между QuicClientConnectionOptions и QuicServerConnectionOptions. Это абстрактный базовый класс и его нельзя использовать самостоятельно. Он содержит следующие свойства:

КодОшибкаЗакрытияПоУмолчанию

DefaultCloseErrorCode используется при удалении подключения без вызова QuicConnection.CloseAsync(Int64, CancellationToken). Он требуется протоколу QUIC для предоставления причины закрытия подключения на уровне приложения (RFC 9000 — закрытие подключения). QuicConnection не имеет способа заставить код приложения вызвать CloseAsync(Int64, CancellationToken) перед освобождением подключения. В таком случае подключение должно знать, какой код ошибки следует использовать. Это свойство является обязательным.

Код ошибки потока по умолчанию

DefaultStreamErrorCode используется при удалении потока перед чтением всех данных. При получении данных по потоку QUIC приложение может либо использовать все данные, либо, если это невозможно, оно должно прервать процесс чтения. И, подобно закрытию соединения, протокол QUIC требует указания причины для прекращения считывания данными (RFC 9000 — Stop Sending с указанием причины). Это свойство является обязательным.

РукопожатиеTimeout

HandshakeTimeout задает ограничение времени, в котором необходимо полностью установить подключение; в противном случае он прерван. Это значение InfiniteTimeSpan можно задать, но это не рекомендуется. Попытки подключения могут зависать на неопределенно долго, и нет никаких способов их устранить, кроме как остановить QuicListener. Это свойство является необязательным, значение по умолчанию — 10 секунд.

IdleTimeout

Если соединение неактивно дольше указанного IdleTimeout, оно отключается. Этот параметр является частью спецификации протокола QUIC (RFC 9000 — тайм-аут бездействия) и отправляется в одноранговый узел во время рукопожатия соединения. Затем подключение принимает меньшее время ожидания из своего значения и значения однорангового узла и использует его. Таким образом, соединение может быть закрыто при истечении времени ожидания простоя раньше, чем указано в этом параметре. Это свойство является необязательным, значение по умолчанию основано на MsQuic, который составляет 30 секунд.

НачальныеРазмерыОкнаПолучения

InitialReceiveWindowSizes задает набор значений, ограничивающий количество данных, изначально получаемых подключением и (или) потоком. Протокол QUIC определяет механизм ограничения количества данных, которые можно отправлять по отдельным потокам, а также совокупно для всего подключения (RFC 9000 — управление потоками данных). Эти ограничения применяются только до того, как приложение начнет потреблять данные. После этого система постоянно настраивает размер окна получения на основе того, MsQuic насколько быстро приложение считывает данные. Это свойство относится к типу QuicReceiveWindowSizes, который содержит такие параметры:

  • Connection: совокупное ограничение для полученных данных во всех потоках, принадлежащих этому соединению.
  • LocallyInitiatedBidirectionalStream: ограничение для полученных данных в исходящем двунаправленном потоке.
  • RemotelyInitiatedBidirectionalStream: ограничение получаемых данных для двунаправленного входящего потока.
  • UnidirectionalStream: ограничение для полученных данных для входящего однонаправленного потока.

Эти значения должны быть неотрицательными целыми числами, являющимися степенью двойки; это наследуемое ограничение от MsQuic. Установка любого из этих значений в значение 0 означает, что никакие данные никогда не будут получаться определенным потоком или подключением в целом. Это свойство является необязательным, значения по умолчанию — 64 КБ для потока и 64 МБ для подключения.

KeepAliveInterval

KeepAliveInterval определяет, следует ли и как часто отправлять кадры PING, чтобы сохранить подключение активным и предотвратить его закрытие IdleTimeout (RFC 9000 — кадры PING). Если вы устанавливаете это свойство, рассмотрите рекомендацию RFC 9000 — отсрочка таймаута бездействия. Установка слишком низкого значения может отрицательно повлиять на производительность. Кроме того, установка свойства слишком близко к времени тайм-аута может привести к прекращению соединения. Это свойство является необязательным, значение по умолчанию означает InfiniteTimeSpan , что ПИН-коды не будут отправлены.

Максимальное число входящих двунаправленных потоков

MaxInboundBidirectionalStreams определяет максимальное число одновременных активных двунаправленных потоков, которые подключение готово принять. Обратите внимание, что это отличается от того, как спецификация QUIC определяет обработку параллелизма (RFC 9000 — управление параллелизмом). Протокол QUIC учитывает потоки совокупно в течение времени существования подключения и использует постоянно увеличивающееся ограничение, чтобы определить общее количество потоков, принятых подключением, включая уже закрытые потоки (RFC 9000 — MAX_STREAMS Кадры). Это свойство упрощает это, чтобы приложение задало только ограничение параллельного потока и MsQuic заботилось о переводе этого ограничения на соответствующие MAX_STREAMS кадры. Это свойство является необязательным, значение по умолчанию — 0 для клиентских подключений и 100 для подключений к серверу.

Максимальное количество входящих однонаправленных потоков

MaxInboundUnidirectionalStreams определяет максимальное число одновременных активных однонаправленных потоков, которые подключение готово принять. Обратите внимание, что это отличается от того, как спецификация QUIC определяет обработку параллелизма потока (RFC 9000 — управление параллелизмом). Протокол QUIC учитывает потоки совокупно в течение времени существования подключения и использует постоянно увеличивающееся ограничение, чтобы определить общее количество потоков, принятых подключением, включая уже закрытые потоки (RFC 9000 — MAX_STREAMS Кадры). Это свойство упрощает это, чтобы приложение задало только ограничение параллельного потока и MsQuic заботилось о переводе этого ограничения на соответствующие MAX_STREAMS кадры. Это свойство является необязательным, значение по умолчанию — 0 для клиентских подключений и 10 для подключений к серверу.

StreamCapacityCallback (Обратный вызов емкости потока)

StreamCapacityCallback — это обратный вызов, который вызывается всякий раз, когда одноранговый узел освобождает новую емкость потока через MAX_STREAMS и, в результате, текущая емкость превышает 0. Значения, предоставленные в аргументах обратного вызова, являются добавочными значениями емкости, то есть сумма всех значений обратного вызова будет равна последнему значению, полученному от MAX_STREAMS (RFC 9000 - MAX_STREAMS Кадры). Этот обратный вызов разработан для поддержки функциональных возможностей SocketsHttpHandler.EnableMultipleHttp3Connections и поставляется с несколькими ограничениями:

  • Приложение должно отслеживать все открывающиеся и уже открытые потоки, чтобы знать фактическую емкость в любое время.
  • Обратный вызов может вызываться параллельно, поэтому приложение должно корректно обрабатывать синхронизацию при подсчёте потоков.
  • Первый вызов (с начальной емкостью) может произойти до QuicConnection, либо через QuicConnection.ConnectAsync(QuicClientConnectionOptions, CancellationToken), либо QuicListener.AcceptConnectionAsync(CancellationToken).

Следующий упрощенный сценарий фиксирует поведение открытия потока и обратного вызова:

  1. Клиент инициирует подключение к серверу через:

    var client = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions
    {
        ...
        StreamCapacityCallback = (connection, args) =>
            Console.WriteLine($"{connection} stream capacity increased by: unidi += {args.UnidirectionalIncrement}, bidi += {args.BidirectionalIncrement}")
    };
    
  2. Сервер отправляет начальные параметры клиенту с ограничением 2 потока для однонаправленных потоков и 0 двунаправленным.

  3. Клиентский StreamCapacityCallback вызывается и выводит на печать:

    [conn][0x58575BF805B0] stream capacity increased by: unidi += 2, bidi += 0
    
  4. Вызов клиента к ConnectAsync возвращается с подключением [conn][0x58575BF805B0].

  5. Клиент пытается открыть несколько потоков:

    var stream1 = await connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
    var stream2 = await connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
    // The following call will get suspended because the stream's limit has been reached.
    var taskStream3 = connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
    
  6. Клиент завершает и закрывает первые два потока:

    await stream1.WriteAsync(data, completeWrites: true);
    await stream1.DisposeAsync();
    await stream2.WriteAsync(data, completeWrites: true);
    await stream2.DisposeAsync();
    Console.WriteLine($"Stream 3 {(taskStream3.IsCompleted ? "opened" : "pending")}");
    
  7. Клиент печатает:

    Stream 3 pending
    
  8. Сервер освобождает дополнительную емкость 2 после обработки первых двух потоков.

  9. На клиенте происходят две вещи. Во-первых, открывается третий поток:

    var stream3 = await taskStream3;
    

    Затем клиент StreamCapacityCallback снова вызывается и печатает:

    [conn][0x58575BF805B0] stream capacity increased by: unidi += 2, bidi += 0
    

Это свойство является необязательным.

Параметры соединения сервера Quic

QuicServerConnectionOptions Параметры предназначены для подключения на стороне сервера. Помимо унаследованных свойств QuicConnectionOptions, он содержит следующее:

ПараметрыАутентификацииСервера

ServerAuthenticationOptions содержит параметры TLS для подключения к серверу. Параметры совпадают с параметрами, используемыми в SslStream.AuthenticateAsServer(SslServerAuthenticationOptions) и SslStream.AuthenticateAsServerAsync(SslServerAuthenticationOptions, CancellationToken). Для сервера QUIC SslServerAuthenticationOptions допустимо, если

Это свойство является обязательным и должно соответствовать перечисленным условиям.

QuicClientConnectionOptions (Опции подключения QuicClient)

QuicClientConnectionOptions Параметры зависят от клиентского подключения. Помимо унаследованных свойств QuicConnectionOptions, он содержит следующее:

Параметры аутентификации клиента

ClientAuthenticationOptions содержит параметр TLS для подключения клиента. Параметры совпадают с параметрами, используемыми в SslStream.AuthenticateAsClient(SslClientAuthenticationOptions) и SslStream.AuthenticateAsClientAsync(SslClientAuthenticationOptions, CancellationToken). Для клиента QUIC SslClientAuthenticationOptions допустимо, если:

Это свойство является обязательным и должно соответствовать перечисленным условиям.

Локальная конечная точка

LocalEndPoint содержит IP-адрес и порт, к которому привязывается клиентское подключение. Если он не указан, ОС назначает IP-адрес и порт. Это свойство является необязательным.

Удаленный конечный пункт

RemoteEndPoint может быть либо DnsEndPoint, либо IPEndPoint однорангового узла, к которому устанавливается соединение. В случае, если это DnsEndPoint, используется первый IP-адрес, возвращаемый Dns.GetHostAddressesAsync(String, CancellationToken). Это свойство является обязательным.