Этот перевод был создан с помощью машинного обучения и может быть не на 100% точным. Просмотреть английскую версию

Streaming Protocol

TCP-like transport used by most I2P applications

Overview

The I2P Streaming Library provides reliable, in-order, authenticated transport over I2P’s message layer, similar to TCP over IP. It sits above the I2CP protocol and is used by nearly all interactive I2P applications, including HTTP proxies, IRC, BitTorrent, and email.

This design enables small HTTP requests and responses to complete in a single round-trip. A SYN packet may carry the request payload, while the responder’s SYN/ACK/FIN may contain the full response body.


The Java streaming API mirrors standard Java socket programming:

Full Javadocs are available from the I2P router console or here .

API Basics


You can pass configuration properties when creating a socket manager via:

Newer features since version 0.9.4 include reject log suppression, DSA list support (0.9.21), and mandatory protocol enforcement (0.9.36). Routers since 2.10.0 include post-quantum hybrid encryption (ML-KEM + X25519) at the transport layer.

Core Characteristics


Each stream is identified by a Stream ID. Packets carry control flags similar to TCP: SYNCHRONIZE, ACK, FIN, and RESET. Packets may contain both data and control flags simultaneously, improving efficiency for short-lived connections.

Because I2P tunnels introduce latency and message reordering, the library buffers packets from unknown or early-arriving streams. Buffered messages are stored until synchronization completes, ensuring complete, in-order delivery.

OptionDefaultNotes
i2cp.accessListnullComma- or space-separated list of Base64 peer Hashes used for either access list or blacklist. As of release 0.7.13.
i2cp.destination.sigTypeDSA_SHA1The name or number of the signature type for a transient destination. As of release 0.9.12.
i2cp.enableAccessListfalseUse the access list as a whitelist for incoming connections. As of release 0.7.13.
i2cp.enableBlackListfalseUse the access list as a blacklist for incoming connections. As of release 0.7.13.
i2p.streaming.answerPingstrueWhether to respond to incoming pings
i2p.streaming.blacklistnullComma- or space-separated list of Base64 peer Hashes to be blacklisted for incoming connections to ALL destinations in the context. This option must be set in the context properties, NOT in the createManager() options argument. Note that setting this in the router context will not affect clients outside the router in a separate JVM and context. As of release 0.9.3.
i2p.streaming.bufferSize64KHow much transmit data (in bytes) will be accepted that hasn't been written out yet.
i2p.streaming.congestionAvoidanceGrowthRateFactor1When we're in congestion avoidance, we grow the window size at the rate of 1/(windowSize*factor). In standard TCP, window sizes are in bytes, while in I2P, window sizes are in messages. A higher number means slower growth.
i2p.streaming.connectDelay-1How long to wait after instantiating a new con before actually attempting to connect. If this is <= 0, connect immediately with no initial data. If greater than 0, wait until the output stream is flushed, the buffer fills, or that many milliseconds pass, and include any initial data with the SYN.
i2p.streaming.connectTimeout5*60*1000How long to block on connect, in milliseconds. Negative means indefinitely. Default is 5 minutes.
i2p.streaming.disableRejectLoggingfalseWhether to disable warnings in the logs when an incoming connection is rejected due to connection limits. As of release 0.9.4.
i2p.streaming.dsalistnullComma- or space-separated list of Base64 peer Hashes or host names to be contacted using an alternate DSA destination. Only applies if multisession is enabled and the primary session is non-DSA (generally for shared clients only). This option must be set in the context properties, NOT in the createManager() options argument. Note that setting this in the router context will not affect clients outside the router in a separate JVM and context. As of release 0.9.21.
i2p.streaming.enforceProtocoltrueWhether to listen only for the streaming protocol. Setting to true will prohibit communication with Destinations earlier than release 0.7.1 (released March 2009). Set to true if running multiple protocols on this Destination. As of release 0.9.1. Default true as of release 0.9.36.
i2p.streaming.inactivityAction2 (send)(0=noop, 1=disconnect) What to do on an inactivity timeout - do nothing, disconnect, or send a duplicate ack.
i2p.streaming.inactivityTimeout90*1000Idle time before sending a keepalive
i2p.streaming.initialAckDelay750Delay before sending an ack
i2p.streaming.initialResendDelay1000The initial value of the resend delay field in the packet header, times 1000. Not fully implemented; see below.
i2p.streaming.initialRTO9000Initial timeout (if no sharing data available). As of release 0.9.8.
i2p.streaming.initialRTT8000Initial round trip time estimate (if no sharing data available). Disabled as of release 0.9.8; uses actual RTT.
i2p.streaming.initialWindowSize6(if no sharing data available) In standard TCP, window sizes are in bytes, while in I2P, window sizes are in messages.
i2p.streaming.limitActionresetWhat action to take when an incoming connection exceeds limits. Valid values are: reset (reset the connection); drop (drop the connection); or http (send a hardcoded HTTP 429 response). Any other value is a custom response to be sent. backslash-r and backslash-n will be replaced with CR and LF. As of release 0.9.34.
i2p.streaming.maxConcurrentStreams-1(0 or negative value means unlimited) This is a total limit for incoming and outgoing combined.
i2p.streaming.maxConnsPerMinute0Incoming connection limit (per peer; 0 means disabled). As of release 0.7.14.
i2p.streaming.maxConnsPerHour0(per peer; 0 means disabled). As of release 0.7.14.
i2p.streaming.maxConnsPerDay0(per peer; 0 means disabled). As of release 0.7.14.
i2p.streaming.maxMessageSize1730The maximum size of the payload, i.e. the MTU in bytes.
i2p.streaming.maxResends8Maximum number of retransmissions before failure.
i2p.streaming.maxTotalConnsPerMinute0Incoming connection limit (all peers; 0 means disabled). As of release 0.7.14.
i2p.streaming.maxTotalConnsPerHour0(all peers; 0 means disabled) Use with caution as exceeding this will disable a server for a long time. As of release 0.7.14.
i2p.streaming.maxTotalConnsPerDay0(all peers; 0 means disabled) Use with caution as exceeding this will disable a server for a long time. As of release 0.7.14.
i2p.streaming.maxWindowSize128
i2p.streaming.profile1 (bulk)1=bulk; 2=interactive; see important notes below.
i2p.streaming.readTimeout-1How long to block on read, in milliseconds. Negative means indefinitely.
i2p.streaming.slowStartGrowthRateFactor1When we're in slow start, we grow the window size at the rate of 1/(factor). In standard TCP, window sizes are in bytes, while in I2P, window sizes are in messages. A higher number means slower growth.
i2p.streaming.tcbcache.rttDampening0.75Ref: RFC 2140. Floating point value. May be set only via context properties, not connection options. As of release 0.9.8.
i2p.streaming.tcbcache.rttdevDampening0.75Ref: RFC 2140. Floating point value. May be set only via context properties, not connection options. As of release 0.9.8.
i2p.streaming.tcbcache.wdwDampening0.75Ref: RFC 2140. Floating point value. May be set only via context properties, not connection options. As of release 0.9.8.
i2p.streaming.writeTimeout-1How long to block on write/flush, in milliseconds. Negative means indefinitely.
## Configuration and Tuning

The option i2p.streaming.enforceProtocol=true (default since 0.9.36) ensures connections use the correct I2CP protocol number, preventing conflicts between multiple subsystems sharing one destination.

Protocol Details

Key Options


The streaming protocol coexists with the Datagram API, giving developers the choice between connection-oriented and connectionless transports.

Behavior by Workload

Applications can reuse existing tunnels by running as shared clients, allowing multiple services to share the same destination. While this reduces overhead, it increases cross-service correlation risk—use with care.

Because I2P adds several hundred milliseconds of base latency, applications should minimize round-trips. Bundle data with connection setup where possible (e.g., HTTP requests in SYN). Avoid designs relying on many small sequential exchanges.


Performance depends heavily on tunnel configuration: - Short tunnels (1–2 hops) → lower latency, reduced anonymity. - Long tunnels (3+ hops) → higher anonymity, increased RTT.

Connection Lifecycle


Fragmentation and Reordering


Protocol Enforcement

The I2P Streaming Library is the backbone of all reliable communication within I2P. It ensures in-order, authenticated, encrypted message delivery and provides a near drop-in replacement for TCP in anonymous environments.

Shared Clients

To achieve optimal performance: - Minimize round-trips with SYN+payload bundling. - Tune window and timeout parameters for your workload. - Favor shorter tunnels for latency-sensitive applications. - Use congestion-friendly designs to avoid overloading peers.

Размер окна передачи и приема по умолчанию в Java-реализации составляет максимум 128 пакетов. Реализации, устанавливающие максимальный размер окна передачи выше 128, должны учитывать следующие проблемы:

  • One-phase connection setup using SYN, ACK, and FIN flags that can be bundled with payload data to reduce round-trips.
  • Sliding-window congestion control, with slow start and congestion avoidance tuned for I2P’s high-latency environment.
  • Packet compression (default 4KB compressed segments) balancing retransmission cost and fragmentation latency.
  • Fully authenticated, encrypted, and reliable channel abstraction between I2P destinations.

Рекомендуемый минимальный размер буфера для реализаций получателя составляет 128 пакетов или 232 КБ (приблизительно 128 * 1812). Из-за задержек в сети I2P, потерь пакетов и связанного с этим контроля перегрузки буфер такого размера редко заполняется. Однако переполнение гораздо более вероятно при высокоскоростных соединениях “local loopback” (внутри одного router) или при локальном тестировании.

Для быстрого обнаружения и плавного восстановления после состояний переполнения в потоковом протоколе существует простой механизм отталкивания. Если получен пакет с опциональным полем задержки со значением 60001 или выше, это указывает на “удушение” или нулевое окно приема. Пакет с опциональным полем задержки со значением 60000 или меньше указывает на “разблокировку”. Пакеты без опционального поля задержки не влияют на состояние блокировки/разблокировки.

После того как соединение заблокировано (choked), никакие пакеты с данными не должны отправляться до разблокировки (unchoked), за исключением редких “зондирующих” пакетов данных для компенсации возможных потерянных пакетов разблокировки. Заблокированная конечная точка должна запустить “таймер настойчивости” для управления зондированием, как в TCP. Разблокирующая конечная точка должна отправить несколько пакетов с установленным полем, или продолжать отправлять их периодически, пока пакеты данных не начнут поступать снова. Максимальное время ожидания разблокировки зависит от реализации. Размер окна передатчика и стратегия управления перегрузкой после разблокировки зависят от реализации.

Congestion Control

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

Latency Considerations

Любой пакет, включая пакет с установленным флагом SYNCHRONIZE, также может иметь установленный флаг CLOSE. Соединение не закрывается до тех пор, пока узел не ответит флагом CLOSE. Пакеты CLOSE также могут содержать данные.

Ping / Pong

На уровне I2CP нет функции ping (эквивалентной ICMP echo) или в датаграммах. Эта функция предоставляется в streaming. Ping и pong не могут быть объединены со стандартным streaming пакетом; если установлена опция ECHO, то большинство других флагов, опций, ackThrough, sequenceNum, NACK и т.д. игнорируются.

Пакет ping должен иметь установленные флаги ECHO, SIGNATURE_INCLUDED и FROM_INCLUDED. Поле sendStreamId должно быть больше нуля, а receiveStreamId игнорируется. Поле sendStreamId может соответствовать или не соответствовать существующему соединению.

Пакет pong должен иметь установленный флаг ECHO. Поле sendStreamId должно быть равно нулю, а receiveStreamId равно значению sendStreamId из пакета ping. До релиза 0.9.18 пакет pong не включал никакой полезной нагрузки, которая содержалась в ping.

Начиная с версии 0.9.18, пинги и понги могут содержать полезную нагрузку. Полезная нагрузка в пинге, до максимум 32 байт, возвращается в понге.

Streaming может быть настроен для отключения отправки pong с помощью конфигурации i2p.streaming.answerPings=false.

Проблемы 0-RTT

Как отмечалось выше, в отличие от TCP, streaming позволяет доставлять данные с 0-RTT, упаковывая данные в SYN пакет. Это предпочтительная реализация. ТАКЖЕ streaming разрешает отправку дополнительных пакетов данных (до размера начального окна) после SYN, до получения SYN-ACK. Эти пакеты будут иметь ненулевой порядковый номер, не будут иметь установленного флага SYN и будут иметь нулевой sendStreamID.

Получатели должны учитывать возможность получения пакетов не по порядку или потери пакетов во время handshake, включая ситуации, когда пакеты данных приходят раньше SYN. Предпочтительная реализация — ставить в очередь, а не отбрасывать пакеты, отличные от SYN, для неизвестного ID, и извлекать их из очереди после получения SYN.

В обратном направлении всё происходит аналогично. Получатель соединения (Bob) должен задержать отправку SYN-ACK (ACK DELAY) и немного подождать данные от приложения. При получении данных от приложения поместить их (до максимального размера пакета) в пакет SYN-ACK и отправить его. Дополнительные пакеты данных, до размера начального окна, также могут быть отправлены без ожидания ACK для SYN-ACK.

Инициатор должен буферизовать любые пакеты данных, полученные до SYN-ACK, так же как и обработка пакетов, полученных не по порядку после завершения handshake.

Тестирование библиотек потокового вещания

Для разработчиков, тестирующих новые или измененные библиотеки потоковой передачи, Java I2P предоставляет простую локальную тестовую утилиту для воспроизводимого тестирования реальных сетевых условий, включая задержку, потерю пакетов и джиттер задержки. Это небольшая заглушка, реализующая только I2CP сервер для локальных подключений.

Разработчики должны тестировать с широким диапазоном типичных параметров, включая задержку от 10 мс до как минимум 15 с и потери пакетов от 0 до 10%. Добавление джиттера упрощает тестирование обработки пакетов, поступающих не по порядку.

Это также хорошая настройка для тестирования переполнения буфера (CHOKE/UNCHOKE) путем ручной приостановки одного из двух приложений.

Из исходного пакета i2p.i2p:

  • I2PSocketManagerFactory negotiates or reuses a router session via I2CP.
  • If no key is provided, a new destination is automatically generated.
  • Developers can pass I2CP options (e.g., tunnel lengths, encryption types, or connection settings) through the options map.
  • I2PSocket and I2PServerSocket mirror standard Java Socket interfaces, making migration straightforward.

i2p.streaming.profile Примечания

Эта опция поддерживает два значения: 1=bulk и 2=interactive. Опция предоставляет подсказку библиотеке потоковой передачи и/или router относительно ожидаемого шаблона трафика.

“Bulk” означает оптимизацию для высокой пропускной способности, возможно за счет задержки. Это значение по умолчанию. “Interactive” означает оптимизацию для низкой задержки, возможно за счет пропускной способности или эффективности. Стратегии оптимизации, если таковые имеются, зависят от реализации и могут включать изменения за пределами протокола потоковой передачи.

До версии API 0.9.63 Java I2P возвращал ошибку для любого значения, отличного от 1 (bulk), и tunnel не удавалось запустить. Начиная с версии API 0.9.64, Java I2P игнорирует это значение. До версии API 0.9.63 i2pd игнорировал эту опцию; она реализована в i2pd начиная с версии API 0.9.64.

Хотя протокол потоковой передачи включает поле флагов для передачи настроек профиля на другую сторону, это не реализовано ни в одном из известных router’ов.

Совместное использование блока управления

Библиотека потоковой передачи поддерживает совместное использование блоков управления “TCP”. Это позволяет совместно использовать три важных параметра библиотеки потоковой передачи (размер окна, время круговой задержки, дисперсия времени круговой задержки) между соединениями с одним и тем же удаленным узлом. Это используется для “временного” совместного использования во время открытия/закрытия соединения, а не для “ансамблевого” совместного использования во время соединения (См. RFC 2140 ). Существует отдельное совместное использование для каждого ConnectionManager (т.е. для каждого локального Destination), чтобы не было утечки информации к другим Destination на том же router. Данные совместного использования для данного узла истекают через несколько минут. Следующие параметры совместного использования блоков управления могут быть установлены для каждого router:

  1. SYN sent — initiator includes optional data.
  2. SYN/ACK response — responder includes optional data.
  3. ACK finalization — establishes reliability and session state.
  4. FIN/RESET — used for orderly closure or abrupt termination.

Другие параметры

Следующие параметры являются рекомендуемыми по умолчанию. Значения по умолчанию могут варьироваться в зависимости от реализации:

  • The streaming layer continuously adapts to network latency and throughput via RTT-based feedback.
  • Applications perform best when routers are contributing peers (participating tunnels enabled).
  • TCP-like congestion control mechanisms prevent overloading slow peers and help balance bandwidth use across tunnels.

История

Библиотека потоковой передачи развивалась органически для I2P - сначала mihi реализовал “мини-библиотеку потоковой передачи” как часть I2PTunnel, которая была ограничена размером окна в 1 сообщение (требуя подтверждения ACK перед отправкой следующего), а затем она была выделена в обобщённый интерфейс потоковой передачи (имитирующий TCP-сокеты) и полная реализация потоковой передачи была развёрнута с протоколом скользящего окна и оптимизациями для учёта высокого произведения пропускной способности на задержку. Отдельные потоки могут настраивать максимальный размер пакета и другие параметры. Размер сообщения по умолчанию выбран так, чтобы точно помещаться в два I2NP tunnel сообщения по 1К, и представляет разумный компромисс между затратами пропускной способности на повторную передачу потерянных сообщений и задержкой и накладными расходами множественных сообщений.

Interoperability and Best Practices

Поведение библиотеки потоковой передачи оказывает глубокое влияние на производительность на уровне приложений и поэтому является важной областью для дальнейшего анализа.

  • Always test against both Java I2P and i2pd to ensure full compatibility.
  • Although the protocol is standardized, minor implementation differences may exist.
  • Handle older routers gracefully—many peers still run pre-2.0 versions.
  • Monitor connection stats using I2PSocket.getOptions() and getSession() to read RTT and retransmission metrics.

Was this page helpful?