Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) classReusable event to send an ACK for a duplicate packet after a short delay.(package private) classNotify waiters to reschedule the connection event.(package private) classTimer event that resends a single outbound packet when it fires and then offers itself back to_resendEventPoolfor reuse.(package private) classA single retransmit timer for all packets.(package private) static enumVerdict of one floor evaluation.Field Summary
FieldsModifier and TypeFieldDescription(package private) longFixed-point congestion-avoidance credit for the deterministic growth ratchet inConnectionPacketHandler.adjustWindow.static final intAbsolute ceiling on in-flight packets regardless of BDP estimate.(package private) static final longMinimum connect window (ms) after Tuner-multiplier scaling, so a fast network never cuts a handshake below a full SYN retransmit cycle.static final intDefault base connect timeout in milliseconds (30 seconds), the value used for an outbound connect that has none configured.(package private) static final StringAccurate error set when the remote closes an outbound connection before it was established.(package private) static final StringAccurate error reported bywaitForConnect(int)on its own timeout.(package private) static final StringAccurate error set when an established data packet exceeds its retransmit limit.(package private) static final StringAccurate error set when an outbound SYN is never acknowledged within its retransmit budget.(package private) static final intIf we have been explicitly NACKed three times, retransmit the packet even if there are other packets in flight.(package private) static final intNumber of retransmits sent directly per RTO fire before remaining retransmits are paced, to avoid flooding the I2CP queue with a single-timer-fire burst.(package private) static final intOnce a packet has been transmitted this many times, always send it directly instead of pacing it: a repeatedly-resent packet is recovery-critical and must not starve behind a stale pacing rate in the paced queue (hard drain deadline).(package private) static final intThe longest min-RTT believed to be a measurement.static final intDefault window size cap used when no per-connection or global override is set.(package private) static final intWindow size below which pacing is bypassed to avoid the stale-initial-RTT throttle.(package private) static final intFloor for the remote-silence bound (remoteSilentTooLong(long, int, long)) when no positive inactivity window is configured: the protocol's default inactivity window (120s).(package private) static final intMinimum RTT (ms) used when deriving the bandwidth slow-start threshold anchor.(package private) static final intMaximum number of SYN-ACK re-sends to an existing connection in response to retransmitted SYNs, per connection lifetime.(package private) static final longMinimum spacing (ms) between SYN-ACK re-sends to an existing connection in response to retransmitted SYNs.(package private) static final intMinimum SYN retransmit interval (ms) ever used once positive RTT evidence for the path exists.Constructor Summary
ConstructorsConstructorDescriptionConnection(I2PAppContext ctx, ConnectionManager manager, I2PSession session, SchedulerChooser chooser, SimpleTimer2 timer, PacketQueue queue, ConnectionPacketHandler handler, ConnectionOptions opts, boolean isInbound) Constructor for this connection.Method Summary
Modifier and TypeMethodDescription(package private) voidAck immediately.ackPackets(long ackThrough, long[] nacks) Process the acks and nacks received in a packet.(package private) static intbandwidthSsthreshAnchor(float bwEstPacketsPerMs, int minRttMs, int factor, int floor) The bandwidth-derived slow-start threshold anchor: the packets the pipe can hold, estimated as measured bandwidth times the effective RTT times the growth factor, floored at the given minimum.(package private) static booleanbudgetExhaustionClosesConnection(boolean budgetExhausted, boolean hasForwardProgress) Pure decision: should an exhausted per-packet retransmit budget close the connection?(package private) booleanClaim this connection's slot release, once per generation.(package private) voidClears the pending tunnel-rotation flag, so the next soft-failure pass does not rotate again.voidNotify that a close was received.(package private) static longcomputeConnectBase(long connectTimeoutMs, long connectDelayMs) Assemble the un-scaled base connect window (ms) for a connection's options.(package private) static longcomputeEffectiveConnectTimeout(long desiredTimeoutMs, int multiplier, long maxConnectTimeoutMs) Compute the effective connect timeout (ms) an outbound connect will wait, as used by bothwaitForConnect(int)and the SYN give-up budget ingetMaxSynSends().(package private) static intcomputeSynAckResendCap(long connectWindowMs, long spacingMs) Compute the per-connection SYN-ACK re-send cap: the number of retransmitted SYNs the client can fit within its effective connect window, given the inter-SYN interval in use.(package private) static intcomputeSynResendBudget(int configuredMax, int synIntervalMs, long connectWindowMs) Compute the effective number of SYN sends before this connection gives up, used bygetMaxSynSends()on each give-up decision.(package private) static intcomputeSynRetransmitInterval(int measuredRttMs, int initialRtoMs) Compute the inter-SYN retransmit interval for an outbound connection, evidence-gated and RTT-aware.(package private) static intcomputeWindowCeiling(int globalMax, float bwePerMs, int rttMs, int floorMsgs, int absMaxMsgs) Pure decision helper forgetWindowCeiling(): the per-stream window ceiling in messages —min(absMax, max(globalMax, floor, headroom * bwe * rtt)), falling back tomin(absMax, max(globalMax, floor))when no bandwidth sample exists yet.(package private) static intdecayLossStrikes(int strikes, long lastLossTime, long now, long smoothedRtt) Strike decay: a single loss strike is forgiven once at least one smoothed RTT (floored at 500 ms) has passed without another strike, rolling the graduated backoff back down toward the gentle tier.voiddisconnect(boolean cleanDisconnect) Must be called when we are done with this connection.voiddisconnect(boolean cleanDisconnect, boolean removeFromConMgr) Must be called when we are done with this connection.voidMust be called when we are done with this connection.(package private) static inteffectiveInactivityTimeout(int configured, int fallbackMs) Effective inactivity window for the remote-silence bound.(package private) voidNotify the scheduler that an event occurred on this connection.(package private) Connection.AckDupEventSchedule an event on our timer.longHow many packets have we sent and the other side has ACKed?longTimestamp when a close was received.longTimestamp when a close was sent.longCongestion window end sequence number.Connection error message, if any.The ConnectionManager that created this connection.(package private) static intCurrent Tuner-scaled connect timeout percentage.longTimestamp when this connection was created.longTimestamp when the disconnect was scheduled.static intIdle time after which an established connection is dropped (i2p.streaming.disconnectTimeout) in ms.(package private) longThe total time (ms) from connection creation during which this connecting connection may wait for its SYN to be acknowledged before the connect path tears the handshake down.(package private) static longRouter-wide absolute cap on the effective connect window, in milliseconds.static intCeiling floor every stream ramps at least to, whatever its own BDP says.booleanCheck if this connection has been hard-disconnected (via RESET).longHighest outbound packet we have received an ack for.Input stream that the local peer receives data on.booleanAlways true at the start, even if we haven't gotten a reply on an outbound connection.longTimestamp of the last send or receive activity.longWhat was the last packet Id sent to the peer?longWhen did we last send anything to the peer?longLifetime of this connection in milliseconds.longTotal bytes received on this connection.longTotal bytes sent on this connection.longTotal duplicate bytes sent on this connection.longTotal duplicate messages received on this connection.longTotal duplicate messages sent on this connection.intLocal port of this connection.(package private) longAbsolute cap on this connection's connect window, in milliseconds.static intDefault resend delay (i2p.streaming.maxResendDelay) in ms.static intMaximum packets to retransmit per timer fire.static intConfigured give-up count for an unacknowledged SYN.static intDefault resend delay (i2p.streaming.minResendDelay) in ms.longNext outbound packet sequence number.longTime when the scheduler next want to send a packet, or -1 if never.Retrieve the current ConnectionOptions.Output stream that the local peer sends data to the remote peer on.Packet handler for this connection.intgetPort()The remote port.longThe stream ID of a peer connection that sends data to us, or zero if unknown.Destination of the remote peer.The key to verify signatures with.booleanCheck if a reset has been received on this connection.longTimestamp when a reset was received.booleanCheck if a reset has been sent on this connection.longTimestamp when a reset was sent.(package private) intReturns the current retransmit count (consecutive retransmit timer firings without ACK progress).longWhat stream do we send data to the peer on?I2P session for this connection.Socket associated with this connection.(package private) intCurrent slow-start threshold: the window at which congestion avoidance takes over from exponential slow start.(package private) longReturns the number of milliseconds between the last send and now, for external stall detection callers.intCount of unacked packets received.intHow many packets have we sent but not yet received an ACK for?(package private) intPer-stream window ceiling: the minimum ofABSOLUTE_MAX_WINDOWand the larger of the Tuner-managed global ceiling (or the per-connection override) and this stream's own bandwidth-delay product scaled byWINDOW_CEILING_HEADROOM_PCT.ForConnectionPacketHandler.adjustWindow(Connection, boolean, long, int, int, boolean).(package private) static intgraduatedLossSsthresh(int strikes, int wsize, int bwBasedSsthresh) Slow-start threshold for a graduated loss response: never below the graduated window (so the connection can regrow to at least its post-cut capacity) and never below the bandwidth-derived estimate.(package private) static intgraduatedLossWindow(int strikes, int wsize) Graduated congestion response: the window is cut by an amount that scales with the number of CONSECUTIVE loss events since the last recovery, so a single failed packet does not collapse the connection while persistent loss still backs off.(package private) static booleanhardResendBudgetExceeded(int totalSends, int softResends, int maxResends) Pure decision: has the number of HARD send attempts (sends that took the router's reply as something to retry after, i.e.voidincrementBytesReceived(int bytes) Increment the lifetime bytes received counter.voidincrementBytesSent(int bytes) Increment the lifetime bytes sent counter.voidincrementDupBytesSent(int bytes) Increment the lifetime duplicate bytes sent counter.voidincrementDupMessagesReceived(int msgs) Increment the lifetime duplicate messages received counter.voidincrementDupMessagesSent(int msgs) Increment the lifetime duplicate messages sent counter.voidIncrement the count of unacked packets received.(package private) static intinitialSsthresh(int maxSlowStartWindow, int globalMaxWindow, int initialWindowSize) The slow-start threshold a freshly constructed connection starts with, clamped so it can never exceed the connection's window ceiling.booleanisChoked()Whether the other side is choking us.booleanCheck if this is an inbound connection.(package private) booleanReturns true if the next send should use a fresh connection (tunnel rotation).(package private) booleanReturns true if the retransmit count exceeds the stall threshold, indicating a stalled download on a slow/lossy path.(package private) static booleanisRetransmitStalledNow(boolean established, long now, long stallSinceMs, long thresholdMs) Decide whether the retransmit timer has gone long enough without forward ACK progress to call it a stall.(package private) static booleanlossEpisodeRecovered(boolean hadCongestionMark, long ackThrough, long lastCongestionHighestUnacked, int activeResends) Whether a congestion episode's lost window has been fully recovered.(package private) Connection.ResendPacketEventnewResendPacketEvent(PacketLocal packet) A new ResendPacketEvent from the pool or a fresh instance.(package private) static intnextRetransmitDelay(long now, long oldestLastSend, long rto, boolean immediateAlreadyFired) Delay before the next retransmit-timer fire.voidCalled by CPH when a CLOSE packet is sent.voidNotify that a close that we sent, and all previous packets, were acked.(package private) voidCalled when a packet is received on this connection.booleanpacketSendChoke(long timeoutMs) This doesn't "send a choke".(package private) voidrecordSynAckResend(long now) Record that a SYN-ACK was sent for this connection atnow(initial re-send fromConnectionHandler#resendSynAckonly.(package private) static booleanremoteSilentTooLong(long lastReceivedOn, int inactivityTimeout, long now) Pure predicate: has the remote sent nothing at all for a full inactivity window, while unacked packets are still in flight?voidNotify that a reset was received.(package private) voidRe-arm the slot-release claim for a new generation, called byConnectionManager.assignReceiveStreamId(Connection, Hash)when a pooled connection binds a fresh reservation token: the next teardown of this object must be able to claim again.(package private) static intrestoredWindowOnUnchoke(int saved, int ceiling) The window to restore when the remote unchokes us.(package private) static doublesampleRateBps(long bytes, long elapsedMs) Roll the goodput sample forward, returning the rate of the window that just closed.voidscheduleConnectionEvent(long msToWait) Called fromSchedulerImpl.(package private) voidTrigger immediate retransmission of unacked packets after a soft failure (e.g., tunnel expiry, no tunnels).(package private) voidFlush any data that we can.(package private) voidsendPacket(PacketLocal packet) This sends all 'normal' packets (acks and data) for the first time.voidsetChoked(boolean on) Choked state set by the other side.voidsetChoking(boolean on) Choking state toward the other side.voidsetCongestionWindowEnd(long endMsg) Congestion window end sequence number.voidsetConnectionError(String err) Connection error message.(package private) static voidsetConnectTimeoutMultiplier(int pct) Set the connect timeout multiplier, clamped to [30, 200] percent.static voidsetGlobalMaxWindowSize(int val) Raise or lower the ceiling floor shared by every stream.static voidsetMaxRetransmissions(int val) Resend batch size cap, clamped to [8, 128] packets.static voidsetMaxSynResends(int val) Configured SYN give-up count, clamped to [3, 16].(package private) voidSet the flag so the next send through PacketQueue uses a fresh connection, rotating to a different outbound tunnel when the current one has stalled.voidsetNextSendTime(long when) If the next send time is currently >= 0 (i.e.voidsetOptions(ConnectionOptions opts) ConnectionOptions for this connection.voidsetReceiveStreamId(long id) Stream ID that the peer sends data on.voidsetRemotePeer(Destination peer) Remote peer of this connection, non-null.voidsetRemoteTransientSPK(SigningPublicKey transientSPK) Transient signing public key of the remote peer.voidsetSendStreamId(long id) Stream ID that we send data on.voidsetSocket(I2PSocketFull socket) Socket associated with this connection.(package private) static booleanshouldLogPersistWarn(long lastWarnTime, long now, long minIntervalMs) Whether to log another persist-timer auto-unchoke WARN.(package private) static booleanshouldLogStallEdge(boolean stallDetached, boolean alreadyLogged) Whether this firing should produce a stall warning, given the latch state.(package private) static booleanshouldPaceRetx(int burstCount, int nResends) Pure decision: should this packet's retransmit be paced through the paced queue rather than sent directly?(package private) static booleanshouldRateLimitSoftRetransmit(long now, long lastSoftFailRetransmit, long minSpacingMs) Pure decision: is this soft-failure report too close to the last permitted immediate retransmit to fire another now?(package private) booleanshouldResendSynAck(long now) Whether this connection should re-send a SYN-ACK for a retransmitted SYN right now, given its own send history.(package private) static booleanshouldResendSynAck(long lastSynAckSentOn, int resends, long now, long minSpacingMs, int maxResends) Decide whether to re-send a SYN-ACK for an existing connection in response to a retransmitted SYN.(package private) static booleanshouldSampleChokeSizeBegin(int counter) Whether a first-block iteration should emit astream.chokeSizeBeginsample.(package private) static booleanshouldSampleChokeSizeEnd(int counter) Whether a completed packet release should emit astream.chokeSizeEndsample.(package private) static Connection.StallStatestallVerdict(long now, long lastProgressAt, double observedBps, double ratio, int samples, double baselineBps, double absFloorBps, long stallGraceMs) Decide whether a connection is delivering far below its own established rate, is fully stalled, or is healthy.(package private) static Connection.StallStatestallVerdict(long now, long lastProgressAt, double observedBps, double ratio, int samples, double baselineBps, double absFloorBps, long stallGraceMs, boolean outstandingData) Decide the stall verdict for one window, told whether anything is actually in flight to be stalled on.(package private) static booleanstuckLifetimeExceeded(long budgetMs, long now, long createdOn) Pure predicate: has the oldest unacked packet been in flight beyond the worst-case retransmit budget, measured from CREATION?(package private) booleanWhether the "re-sending SYN-ACK" WARN has already been logged for this connection, and mark it logged on first check.(package private) static booleansynGiveUpBudgetExceeded(int totalSends, int softResends, int maxSynSends) Pure decision: has the retransmit loop exceeded the SYN give-up budget after excluding soft-failure-triggered resends?toString()Human-readable summary of this connection.(package private) static doubleupdatedBaseline(double baseline, double sampleBps, int percent) Fold a completed goodput sample into the baseline EWMA.voidUpdate shared options from the TCB cache.(package private) voidWait for the connection to be established, with the option's connectTimeout.(package private) voidwaitForConnect(int timeoutMs) Wait for the connection to be established, but no longer than timeoutMs.(package private) voidNotify all threads waiting inpacketSendChoke(long).
Field Details
_caWindowAccumulator
long _caWindowAccumulatorFixed-point congestion-avoidance credit for the deterministic growth ratchet inConnectionPacketHandler.adjustWindow. Increments are derived from accumulated ACK credit instead of a per-ACK random draw, giving the same expected growth rate as the probabilistic gate without RNG noise. Holds the 20-bit fractional remainder of the accumulator. Must only be read/written while holding_outboundPacketsLock; zeroed in retransmit() when the window is cut.DEFAULT_CONNECT_TIMEOUT
public static final int DEFAULT_CONNECT_TIMEOUTDefault base connect timeout in milliseconds (30 seconds), the value used for an outbound connect that has none configured. It is not the effective connect window: that is this value (plus the connect delay on the delayed-SYN path) scaled bygetConnectTimeoutMultiplier()and capped atgetMaxConnectTimeout().- See Also:
MAX_WINDOW_SIZE_DEFAULT
public static final int MAX_WINDOW_SIZE_DEFAULTDefault window size cap used when no per-connection or global override is set. This global is the guaranteed floor of the per-stream ceiling (getWindowCeiling()), which the Tuner adjusts based on observed RTT, bandwidth, loss, and memory pressure — every stream ramps at least to this value, while its own BDP may lift the ceiling towardABSOLUTE_MAX_WINDOWso a fast stream is not held back by signals gathered from other streams. Raised from 1024 to 2048 to better utilize high-BDP paths.ABSOLUTE_MAX_WINDOW
public static final int ABSOLUTE_MAX_WINDOWAbsolute ceiling on in-flight packets regardless of BDP estimate. Prevents runaway window growth from estimator noise or bugs. Set to accommodate high-BDP paths (e.g. 50Mbps @ 1s RTT ~ 6250 packets @ 1KB). Raised from 4096 to 8192 for high-throughput bulk transfers.- See Also:
SS_THRESH_BW_ANCHOR_MIN_RTT
static final int SS_THRESH_BW_ANCHOR_MIN_RTTMinimum RTT (ms) used when deriving the bandwidth slow-start threshold anchor. A raw sub-second min-RTT would shrink the anchor toward the degenerate 16-packet floor after the first loss event, locking a healthy pipe at ~10KB/s.- See Also:
MAX_PLAUSIBLE_MIN_RTT_MS
static final int MAX_PLAUSIBLE_MIN_RTT_MSThe longest min-RTT believed to be a measurement. Anything at or above it is treated as the never-measured sentinel rather than a reading: a real minimum round trip inside the network is seconds at the very most, and the sentinel is 24 days. Without the distinction the bandwidth anchor is computed against the sentinel and the result is not a bandwidth estimate at all.- See Also:
CONNECT_TIMEOUT_FLOOR_MS
static final long CONNECT_TIMEOUT_FLOOR_MSMinimum connect window (ms) after Tuner-multiplier scaling, so a fast network never cuts a handshake below a full SYN retransmit cycle.- See Also:
ERR_SYN_NOT_ACKNOWLEDGED
Accurate error set when an outbound SYN is never acknowledged within its retransmit budget.- See Also:
ERR_RETRANSMIT_LIMIT
Accurate error set when an established data packet exceeds its retransmit limit.- See Also:
ERR_CONNECTION_REFUSED
Accurate error set when the remote closes an outbound connection before it was established.- See Also:
ERR_CONNECTION_TIMED_OUT
Accurate error reported bywaitForConnect(int)on its own timeout.- See Also:
SYN_RTO_MIN
static final int SYN_RTO_MINMinimum SYN retransmit interval (ms) ever used once positive RTT evidence for the path exists. Keeps a fresh SYN from re-firing faster than the fabric can plausibly round-trip even on a very fast tunnel, avoiding a retransmit storm on a peer that is merely slow to complete the handshake.- See Also:
SYN_ACK_RESEND_MIN_SPACING_MS
static final long SYN_ACK_RESEND_MIN_SPACING_MSMinimum spacing (ms) between SYN-ACK re-sends to an existing connection in response to retransmitted SYNs.This is the floor: the live spacing used by
shouldResendSynAck(long)is the connection's owngetSynRetransmitInterval()— the same RTT-aware interval the client uses for its SYN retransmit timer — so every client retransmit can receive a SYN-ACK response even as the Tuner adapts the initial RTO. At 3s the server skipped every other retransmit, starving lossy I2P paths where the initial SYN-ACK was lost and each re-send must traverse a degraded tunnel.The amplification bound is preserved by the resend cap (
computeSynAckResendCap(long, long)): spacing ensures one answer per RTO, the count cap hard-bounds the total.- See Also:
SYN_ACK_RESEND_MAX
static final int SYN_ACK_RESEND_MAXMaximum number of SYN-ACK re-sends to an existing connection in response to retransmitted SYNs, per connection lifetime.Used when no connection window is configured (connect timeout absent). The live cap is
computeSynAckResendCap(long, long), derived from the client's effective connect window so a connection that spins in a half-open state (client stuck retransmitting at its RTO, handshake never completes) cannot mint SYN-ACKs past the point the client itself has given up.- See Also:
REMOTE_SILENT_FALLBACK_MS
static final int REMOTE_SILENT_FALLBACK_MSFloor for the remote-silence bound (remoteSilentTooLong(long, int, long)) when no positive inactivity window is configured: the protocol's default inactivity window (120s). A connection must never be held past this on a dead path just because an options object carries a non-positive timeout.- See Also:
FAST_RETRANSMIT_THRESHOLD
static final int FAST_RETRANSMIT_THRESHOLDIf we have been explicitly NACKed three times, retransmit the packet even if there are other packets in flight.- See Also:
IMMEDIATE_RETX_BURST
static final int IMMEDIATE_RETX_BURSTNumber of retransmits sent directly per RTO fire before remaining retransmits are paced, to avoid flooding the I2CP queue with a single-timer-fire burst. @since 0.9.71+.- See Also:
MAX_PACED_RETX
static final int MAX_PACED_RETXOnce a packet has been transmitted this many times, always send it directly instead of pacing it: a repeatedly-resent packet is recovery-critical and must not starve behind a stale pacing rate in the paced queue (hard drain deadline). @since 0.9.71+.- See Also:
PACING_SLOWSTART_THRESHOLD
static final int PACING_SLOWSTART_THRESHOLDWindow size below which pacing is bypassed to avoid the stale-initial-RTT throttle. During slow-start the window itself is the bottleneck and the pacing rate (computed fromDEFAULT_INITIAL_RTO = 2s) would cap throughput at ~108 KB/s regardless of actual path capacity.- See Also:
Constructor Details
Connection
public Connection(I2PAppContext ctx, ConnectionManager manager, I2PSession session, SchedulerChooser chooser, SimpleTimer2 timer, PacketQueue queue, ConnectionPacketHandler handler, ConnectionOptions opts, boolean isInbound) Constructor for this connection.- Parameters:
ctx- application context supplying the clock, logger and statsmanager- the owning connection managersession- the I2P session this connection runs overchooser- scheduler used for delayed connectstimer- the shared timer that drives this connection's eventsqueue- the outbound packet queuehandler- the inbound packet handleropts- per-connection options; may be null, a default set is createdisInbound- true for a connection we accepted rather than dialled
Method Details
getMaxResendDelay
public static int getMaxResendDelay()Default resend delay (i2p.streaming.maxResendDelay) in ms.- Returns:
- the router-wide resend delay default in ms
getMinResendDelay
public static int getMinResendDelay()Default resend delay (i2p.streaming.minResendDelay) in ms.- Returns:
- the router-wide resend delay floor in ms
getDisconnectTimeout
public static int getDisconnectTimeout()Idle time after which an established connection is dropped (i2p.streaming.disconnectTimeout) in ms.- Returns:
- the disconnect timeout in ms
getGlobalMaxConnectTimeout
static long getGlobalMaxConnectTimeout()Router-wide absolute cap on the effective connect window, in milliseconds. Defaults to 75 seconds; an operator may raise or lower it withI2PSocketOptions.PROP_MAX_CONNECT_TIMEOUT, and a per-connection override (ConnectionOptions#setMaxConnectTimeout()) wins when set.- Returns:
- the global cap in ms, never negative
getMaxConnectTimeout
long getMaxConnectTimeout()Absolute cap on this connection's connect window, in milliseconds. Uses the per-connection override (ConnectionOptions#getMaxConnectTimeout()) when set, otherwise the router-widei2p.streaming.maxConnectTimeout.- Returns:
- the cap in ms, never negative
setConnectTimeoutMultiplier
static void setConnectTimeoutMultiplier(int pct) Set the connect timeout multiplier, clamped to [30, 200] percent.- Parameters:
pct- the multiplier percentage (100 = no scaling)
getConnectTimeoutMultiplier
static int getConnectTimeoutMultiplier()Current Tuner-scaled connect timeout percentage.- Returns:
- the connect timeout multiplier percentage (100 = no scaling)
computeEffectiveConnectTimeout
static long computeEffectiveConnectTimeout(long desiredTimeoutMs, int multiplier, long maxConnectTimeoutMs) Compute the effective connect timeout (ms) an outbound connect will wait, as used by bothwaitForConnect(int)and the SYN give-up budget ingetMaxSynSends().Scales the desired base window (
connectDelay + connectTimeout, or justconnectTimeout) by the Tuner connect-timeout multiplier, then applies theCONNECT_TIMEOUT_FLOOR_MSfloor and the absolutemaxConnectTimeoutMscap. Both consumers must agree on this value: a SYN retransmit budget shorter than this window was the cause of the spurious "Connection failed" for live-but-slow peers, where the retransmit timer discarded the connection error-free before waitForConnect reached its own timeout.- Parameters:
desiredTimeoutMs- the base window (connectDelay + connectTimeout) in ms; <=0 means no connect timeout is configuredmultiplier- the Tuner connect-timeout multiplier percentage (30-200)maxConnectTimeoutMs- the absolute cap (i2p.streaming.maxConnectTimeout) in ms- Returns:
- the effective window in ms, or 0 when no connect timeout is configured
getGlobalMaxWindowSize
public static int getGlobalMaxWindowSize()Ceiling floor every stream ramps at least to, whatever its own BDP says.- Returns:
- the Tuner-managed global window ceiling, in messages
setGlobalMaxWindowSize
public static void setGlobalMaxWindowSize(int val) Raise or lower the ceiling floor shared by every stream.- Parameters:
val- the global window ceiling in messages, clamped to [128, ABSOLUTE_MAX_WINDOW]
isRetransmitStalledNow
static boolean isRetransmitStalledNow(boolean established, long now, long stallSinceMs, long thresholdMs) Decide whether the retransmit timer has gone long enough without forward ACK progress to call it a stall.Time, not a count of firings. RTO doubles on every firing for an established connection, so "two firings" is about three seconds early in a connection's life and over a minute late in one that has been running - the same number meaning two very different things, and always far too eager at the start. A duration has one meaning.
Requires an established session. Before the handshake completes there is no forward ACK progress to be missing, so a connection still setting up cannot be stalled; counting its ordinary SYN retransmissions produced a large volume of warnings about connections that were never broken.
- Parameters:
established- true once any packet has been acknowledgednow- current clock value in msstallSinceMs- clock value at which the current no-progress run began, or 0thresholdMs- how long without progress counts as a stall- Returns:
- true only for an established connection idle past the threshold
shouldLogStallEdge
static boolean shouldLogStallEdge(boolean stallDetached, boolean alreadyLogged) Whether this firing should produce a stall warning, given the latch state.The stall condition holds for as long as the path is stuck and the timer keeps firing, so an unconditional warning emits once per RTO tick. That produced over 90,000 identical lines in under an hour here, which is worse than no report at all: it buries the lines that carry signal and trains the reader to skip the class. One line per episode, and an INFO when it ends.
- Parameters:
stallDetached- true when this firing meets the stall conditionalreadyLogged- true when this episode has already been reported- Returns:
- true only on the rising edge of a stall
nextRetransmitDelay
static int nextRetransmitDelay(long now, long oldestLastSend, long rto, boolean immediateAlreadyFired) Delay before the next retransmit-timer fire.An overdue head-of-line packet is worth exactly one immediate attempt. Before this, every subsequent ACK re-armed the timer with
schedule(0), so on a stalled connection each arriving ACK enqueued another immediate run. With a large population of stalled connections that is enough to keep a two-thread timer permanently saturated - and because the coalesce task shares that pool, every graph in the router stops recording at once. After the first immediate attempt the timer falls back to a full RTO, which still bounds recovery time without letting the queue grow per ACK.Pure decision, extracted so the once-only rule can be pinned by tests.
- Parameters:
now- current clock value in msoldestLastSend- send time of the oldest unacked packet, or 0 if nonerto- the connection's current RTO in msimmediateAlreadyFired- true if an immediate fire already happened for this overdue episode- Returns:
- delay in ms; 0 requests an immediate fire
computeSynRetransmitInterval
static int computeSynRetransmitInterval(int measuredRttMs, int initialRtoMs) Compute the inter-SYN retransmit interval for an outbound connection, evidence-gated and RTT-aware.SYN retransmission uses a fixed interval (no backoff — there is no congestion to manage before the connection is established), so the total SYN budget is
maxSynResends * interval. The historical behavior is a fixedinitialRtoMs(default 9000) for every connection, which overshoots the 30s connect timeout on a dead path (12 * 9s = 108s) and under-utilizes the window on a healthy path whose round-trip is far below the default.Gate. The measured RTT
measuredRttMsis positive only when there is genuine path evidence for this peer (a valid analytic ACK, or a dampened RTT seeded from the TCB cache for a recently-contacted destination — seeTCBShare). When it is<= 0there is no evidence (fresh or blackholed peer —Received: 0throughout, sogetRTT()never advances past its initial default), and this returns the configuredinitialRtoMsunchanged. A path with no measurement is treated conservatively rather than fast-failed, because shrinking the interval without evidence would be guessing.RTT-aware interval. With evidence, the interval becomes
headroomPct% * measuredRttMs, floored atSYN_RTO_MIN(no storm on a fast fabric) and capped at the configuredinitialRtoMs(never worse than the current default, so a genuinely slow-but-alive path keeps its handshake room). A measured RTT below the 9000ms default therefore packs more SYN attempts into the connect window (better odds of catching a momentarily-congested tunnel) and, if the path is dead, lets SYN resends exhaust near the 30s connect timeout instead of running the full configured budget — so the proxy can emit the "Website Unreachable" page sooner.- Parameters:
measuredRttMs- a recent round-trip time for this peer in ms, or <=0 when no RTT evidence is available (never measured, no cache entry)initialRtoMs- the configured initial RTO (i2p.streaming.initialRTO) in ms- Returns:
- the fixed inter-SYN retransmit interval (ms) to use
computeSynResendBudget
static int computeSynResendBudget(int configuredMax, int synIntervalMs, long connectWindowMs) Compute the effective number of SYN sends before this connection gives up, used bygetMaxSynSends()on each give-up decision.The total SYN budget is
sends * synIntervalMs(fixed interval, no backoff). With RTT evidence the interval can collapse towardSYN_RTO_MIN(750ms), so a fixedconfiguredMaxbudget can end well beforewaitForConnect(int)'s window does —12 * 750ms = 9sagainst a 10000ms floor — leaving waitForConnect to mislabel the handshake as "Connection failed". This returns at least enough sends thatsends * synIntervalMs >= connectWindowMs, so the connect-timeout gate (which reports an accurate error) fires first on genuinely-dead paths, while a live-but-slow peer keeps receiving SYN retransmits for the full window.- Parameters:
configuredMax- the configured static give-up count (maxSynResends)synIntervalMs- the fixed inter-SYN retransmit interval (ms) in useconnectWindowMs- the effective connect window fromcomputeEffectiveConnectTimeout(long, int, long); 0 means no connect timeout is configured- Returns:
- the number of sends before giving up, never less than
configuredMax
computeSynAckResendCap
static int computeSynAckResendCap(long connectWindowMs, long spacingMs) Compute the per-connection SYN-ACK re-send cap: the number of retransmitted SYNs the client can fit within its effective connect window, given the inter-SYN interval in use. Answering past this point is wasted work — by then the client has given up on the handshake — so the cap hard-bounds SYN-ACK minting without ever starving a live-but-slow peer.- Parameters:
connectWindowMs- the client's effective connect window (getEffectiveConnectWindow()); <=0 means no window is configured and no cap is appliedspacingMs- the inter-SYN-AACK spacing in use; <=0 means no spacing is derived and no cap is applied- Returns:
- the cap; never less than 1, or
SYN_ACK_RESEND_MAXwhen no window is configured
shouldResendSynAck
static boolean shouldResendSynAck(long lastSynAckSentOn, int resends, long now, long minSpacingMs, int maxResends) Decide whether to re-send a SYN-ACK for an existing connection in response to a retransmitted SYN.Bound is applied before the SYN-ACK is minted so a throttled retransmit costs nothing (no packet object, no signature, no enqueue). The decision is purely a function of connection send history and wall clock, which keeps the hot path branch-free and the rule testable without a running router.
Rules. A re-send is allowed when:
- fewer than
maxResendsre-sends have already been performed (resends < maxResends), and - either no SYN-ACK has been sent yet in connection history (
lastSynAckSentOn <= 0— nothing to space against), or the interval since the last SYN-ACK is at leastminSpacingMs.
- Parameters:
lastSynAckSentOn- wall-clock ms of the last SYN-ACK sent for the connection (initial or re-send), or <=0 if none yetresends- number of SYN-ACK re-sends already performed for this connectionnow- current wall-clock ms (I2PAppContext.clock())minSpacingMs- minimum interval between re-sends in msmaxResends- maximum SYN-ACK re-sends per connection lifetime- Returns:
- true if a SYN-ACK should be re-sent now
- fewer than
shouldResendSynAck
boolean shouldResendSynAck(long now) Whether this connection should re-send a SYN-ACK for a retransmitted SYN right now, given its own send history. Pure delegate overshouldResendSynAck(long, int, long, long, int)using the RTT-aware spacing this connection actually arms, so the heuristic is exercised through the same code path tests cover.- Parameters:
now- current wall-clock ms fromI2PAppContext.clock()- Returns:
- true if a SYN-ACK should be re-sent now
recordSynAckResend
void recordSynAckResend(long now) Record that a SYN-ACK was sent for this connection atnow(initial re-send fromConnectionHandler#resendSynAckonly. The initial handshake SYN-ACK is a normal outbound packet and is not counted here). This snapshot advances the throttle window so subsequent retransmitted SYNs are spacedSYN_ACK_RESEND_MIN_SPACING_MSafter the last answer.- Parameters:
now- wall-clock ms the SYN-ACK was enqueued
synAckWarnAlreadyLogged
boolean synAckWarnAlreadyLogged()Whether the "re-sending SYN-ACK" WARN has already been logged for this connection, and mark it logged on first check. Prevents a retransmit storm against a single stuck connection from flooding the log with identical WARN lines (observed as ~15% of log volume during the tracker-tunnel storm); the first re-send logs WARN, subsequent ones drop to DEBUG.- Returns:
- true if the WARN was already logged (caller should log at DEBUG), false if this is the first re-send (caller should log at WARN)
getMaxRetransmissionsStatic
public static int getMaxRetransmissionsStatic()Maximum packets to retransmit per timer fire.- Returns:
- the resend batch size cap
setMaxRetransmissions
public static void setMaxRetransmissions(int val) Resend batch size cap, clamped to [8, 128] packets.- Parameters:
val- the resend batch size cap in packets
getMaxSynResendsStatic
public static int getMaxSynResendsStatic()Configured give-up count for an unacknowledged SYN.- Returns:
- the SYN give-up count before
getMaxSynSends()scaling
setMaxSynResends
public static void setMaxSynResends(int val) Configured SYN give-up count, clamped to [3, 16].- Parameters:
val- the SYN give-up count
setNextSendFreshConnection
void setNextSendFreshConnection()Set the flag so the next send through PacketQueue uses a fresh connection, rotating to a different outbound tunnel when the current one has stalled. Cleared on first use.isNextSendFreshConnection
boolean isNextSendFreshConnection()Returns true if the next send should use a fresh connection (tunnel rotation). Cleared on read so only the first send in a soft-failure pass triggers rotation.- Returns:
- true if the next send should rotate to a fresh connection
clearNextSendFreshConnection
void clearNextSendFreshConnection()Clears the pending tunnel-rotation flag, so the next soft-failure pass does not rotate again.getRetransmitCount
int getRetransmitCount()Returns the current retransmit count (consecutive retransmit timer firings without ACK progress). Used by external callers to detect stalled downloads.- Returns:
- the consecutive no-progress retransmit firings
isRetransmitStall
boolean isRetransmitStall()Returns true if the retransmit count exceeds the stall threshold, indicating a stalled download on a slow/lossy path.- Returns:
- true when two or more firings have passed with no ACK progress
getTimeSinceLastSend
long getTimeSinceLastSend()Returns the number of milliseconds between the last send and now, for external stall detection callers.- Returns:
- milliseconds elapsed since the last send
computeConnectBase
static long computeConnectBase(long connectTimeoutMs, long connectDelayMs) Assemble the un-scaled base connect window (ms) for a connection's options.connectDelay is added to connectTimeout only on the delayed-SYN path (both set), so the base matches how
ConnectionManager.connect(Destination, ConnectionOptions, I2PSession)invokeswaitForConnect(int)— there the SYN has not yet been sent andconnectDelayis added to the timeout so the handshake is not cut short while the SYN is still waiting to be transmitted.- Parameters:
connectTimeoutMs- the option's connect timeout in ms (<=0 means none)connectDelayMs- the option's connect delay in ms- Returns:
- the base connect window in ms; 0 when no connect timeout is configured
getEffectiveConnectWindow
long getEffectiveConnectWindow()The total time (ms) from connection creation during which this connecting connection may wait for its SYN to be acknowledged before the connect path tears the handshake down.This is the single authoritative connect window shared by
waitForConnect(int),getMaxSynSends(), andSchedulerConnecting, so no path can tear an unacknowledged SYN down before the others have given it the full Tuner-scaled budget. It is the (connectDelay + connectTimeout) base — connectDelay counted only on the delayed-SYN path, exactly asConnectionManager.connect(Destination, ConnectionOptions, I2PSession)invokes waitForConnect — scaled by the Tuner's connect-timeout multiplier, floored atCONNECT_TIMEOUT_FLOOR_MS, and capped at the absolute max connect timeout.Using the raw un-scaled connectTimeout here (as SchedulerConnecting once did) lets the scheduler race ahead of waitForConnect on high-RTT paths where the Tuner has raised the multiplier above 100%, killing the handshake with a generic error instead of the accurate "SYN not acknowledged".
- Returns:
- the effective connect window in milliseconds; 0 when no connect timeout is configured (no budget, caller should not give up on timeout)
getSSThresh
int getSSThresh()Current slow-start threshold: the window at which congestion avoidance takes over from exponential slow start.- Returns:
- the slow-start threshold in messages
initialSsthresh
static int initialSsthresh(int maxSlowStartWindow, int globalMaxWindow, int initialWindowSize) The slow-start threshold a freshly constructed connection starts with, clamped so it can never exceed the connection's window ceiling.ConnectionPacketHandler.adjustWindow()picks its growth path onwindow < ssthresh: below is exponential slow start, at or above is linear congestion avoidance. The ceiling fromcomputeWindowCeiling(int, float, int, int, int)ismin(absMax, max(globalMax, initialWindowSize))before any BDP term, so a connection can never grow past that base. Initialisingssthreshto the Tuner-managedmaxSlowStartWindowtherefore risksssthresh > ceilingwhenever the Tuner has raised the slow-start cap above the window ceiling — and thenwindow < ssthreshis permanently true, the deficit-multiplier recovery path in congestion avoidance never runs, and every window change is governed by the slow-start branch alone.Both inputs are Tuner-tunable over overlapping ranges (
maxWindowSize512-8192,maxSlowStartWindow128-8192), so nothing else maintains thessthresh <= ceilinginvariant; this is where it is enforced. Clamping to the pre-BDP base rather thangetWindowCeiling()keeps the constructor free of the bandwidth estimator, which is not yet constructed at this point.- Parameters:
maxSlowStartWindow- the Tuner-managed slow-start cap, in messagesglobalMaxWindow- the Tuner-managed window ceiling floor (i2p.streaming.maxWindowSize), in messagesinitialWindowSize- the configured starting window, in messages- Returns:
- the initial slow-start threshold in messages, in
[1, maxSlowStartWindow]
getNextOutboundPacketNum
public long getNextOutboundPacketNum()Next outbound packet sequence number.- Returns:
- the next sequence number
packetSendChoke
This doesn't "send a choke". Rather, it blocks if the outbound window is full, thus choking the sender that calls this.Block until there is an open outbound packet slot or the write timeout expires. PacketLocal is the only caller, generally with -1.
A persist timer prevents deadlock when the remote chokes us (window=1) and then never sends an unchoke signal. After RTO (min 5s), we auto-unchoke and allow the retransmission mechanism to restore the window, with exponential backoff so a persistently choked peer does not cause a tight probe loop.
The wait loop polls every 50ms (instead of 250ms) to reduce latency when an ACK arrives to free a window slot.
- Parameters:
timeoutMs- 0 or negative means wait forever, 5 minutes max- Returns:
- true if the packet should be sent, false for a fatal error will return false after 5 minutes even if timeoutMs is <= 0.
- Throws:
IOException- if the peer reset the connection, or either side closed the socket, while waiting for a window slotInterruptedException- if interrupted while waiting on the window lock
getWindowCeiling
int getWindowCeiling()Per-stream window ceiling: the minimum ofABSOLUTE_MAX_WINDOWand the larger of the Tuner-managed global ceiling (or the per-connection override) and this stream's own bandwidth-delay product scaled byWINDOW_CEILING_HEADROOM_PCT. The global value is the FLOOR of the cap. Every stream can always ramp to the Tuner's base capacity — while a proven pipe lifts its own ceiling toward the absolute cap. Each stream responds to its own RTT and delivered-bandwidth estimate, and the Tuner still pulls the floor down under memory or loss pressure whileABSOLUTE_MAX_WINDOWbounds every stream unconditionally.Before any ACK sample exists the ceiling falls back to the global floor, so an uncalibrated stream ramps exactly as before (bounded by slow-start's ssthresh and the global ceiling).
The result is cached for
BDP_CACHE_MSso a persistently full window (the app keeps writing, the choke loop keeps blocking) does not re-lock the synchronized estimator and re-read RTT on every write attempt; the Westwood+ sample only moves on ACK cadence anyway. Both writer threads may populate the cache; a ceiling from either is valid, and ceiling + timestamp are stored as one value (WindowCeilingSample) so a reader can never pair one writer's ceiling with the other writer's newer timestamp — which would pin the cache hit to the older ceiling for extra BDP_CACHE_MS windows — nor invert the age check by pairing a stale timestamp with a fresh ceiling.- Returns:
- max allowed in-flight packets for this stream, at least the global/floor base and never above
ABSOLUTE_MAX_WINDOW
computeWindowCeiling
static int computeWindowCeiling(int globalMax, float bwePerMs, int rttMs, int floorMsgs, int absMaxMsgs) Pure decision helper forgetWindowCeiling(): the per-stream window ceiling in messages —min(absMax, max(globalMax, floor, headroom * bwe * rtt)), falling back tomin(absMax, max(globalMax, floor))when no bandwidth sample exists yet.The Tuner global is the FLOOR of the cap, never its ceiling: a measured-goodput BDP term that binds downward is self-defeating — the window gets pinned at the floor, measured goodput cannot grow past the pinned window, and the ceiling stays pinned there forever (the observed 128-message absorbing state that capped a fast path at ~85 KB/s). With the global as the floor, slow start and congestion avoidance always ramp to at least the Tuner base, the BDP term may lift the cap toward
absMaxMsgsonce the path proves capacity, loss response stays with the graduated window cuts, and the Tuner's memory/loss shrink still lowers the guaranteed base on every stream.- Parameters:
globalMax- Tuner-managed global or per-connection ceiling in messages; the guaranteed floorbwePerMs- Westwood+ estimate in packets/ms; NaN or<= 0means no sample yetrttMs- round-trip time in ms (caller applies the 500ms floor)floorMsgs- minimum ceiling regardless of the estimate (the initial window, so the cap is never below a connection's starting cwnd)absMaxMsgs- absolute ceiling regardless of inputs (ABSOLUTE_MAX_WINDOW)- Returns:
- ceiling in messages, in
[min(absMax, max(1, globalMax, floorMsgs)), absMaxMsgs]
shouldLogPersistWarn
static boolean shouldLogPersistWarn(long lastWarnTime, long now, long minIntervalMs) Whether to log another persist-timer auto-unchoke WARN. Guard: emit at most one WARN per persist interval per connection. The persist branch inpacketSendChoke(long)is re-armed on every block and its backoff is a per-call local, so an application that keeps writing into a persistently choked window would otherwise log on every write attempt.- Parameters:
lastWarnTime- last timestamp the WARN was emitted, 0 if nevernow- router clock time in millisecondsminIntervalMs- minimum ms between WARNs (the persist delay itself)- Returns:
- true if the WARN should be emitted
shouldSampleChokeSizeEnd
static boolean shouldSampleChokeSizeEnd(int counter) Whether a completed packet release should emit astream.chokeSizeEndsample.Pure helper for the send branch of
packetSendChoke(long)so the sampling cadence is unit-testable without a router. The caller increments its counter once per successful send and emits when this returns true.- Parameters:
counter- post-increment value of the per-connection end-sample counter- Returns:
- true if this release should be recorded
shouldSampleChokeSizeBegin
static boolean shouldSampleChokeSizeBegin(int counter) Whether a first-block iteration should emit astream.chokeSizeBeginsample.Pure helper for the wait branch of
packetSendChoke(long). The stat exists to tell the Tuner how deep the outbound window was when we started blocking, so it must be emitted whilestartedis still false — that is, on the iterations that actually observe the window full.The prior expression combined
!startedwith a power-of-two mask on a counter incremented in the same statement, so the only iterations that could satisfy both were the 1st (counter=1) and the 17th — and the 17th hasstarted == trueand is filtered out. No iteration could ever emit, leaving the stat permanently zero and every Tuner parameter readingstream.chokeSizeBeginpinned at its default.- Parameters:
counter- post-increment value of the per-connection begin-sample counter- Returns:
- true if this first block should be recorded
stallVerdict
static Connection.StallState stallVerdict(long now, long lastProgressAt, double observedBps, double ratio, int samples, double baselineBps, double absFloorBps, long stallGraceMs) Decide whether a connection is delivering far below its own established rate, is fully stalled, or is healthy.Pure, so the arming rules are testable without a router. The order matters: a hard stall is reported even when the baseline is unknown, because "no bytes at all" is unambiguous on its own, while a throttle verdict is only available once there is a baseline to compare against.
A throttle requires
observedBps > 0. A window that saw no bytes is either a stall — which the grace above already covers — or simply a quiet window, and a latency-bound stream produces those routinely, so scoring them as throttled would report a fault on a healthy path.- Parameters:
now- current clock valuelastProgressAt- clock value of the last observed progressobservedBps- goodput measured over the current sample windowratio- fraction of baseline below which the link is throttledsamples- completed goodput samples backing the baselinebaselineBps- EWMA baseline, 0 if not yet establishedabsFloorBps- absolute floor capping the proportional floorstallGraceMs- time with no bytes before a hard stall is reported- Returns:
- the verdict; never null
stallVerdict
static Connection.StallState stallVerdict(long now, long lastProgressAt, double observedBps, double ratio, int samples, double baselineBps, double absFloorBps, long stallGraceMs, boolean outstandingData) Decide the stall verdict for one window, told whether anything is actually in flight to be stalled on.outstandingDatais the guard against the false positive that made this detector harmful: a download that is simply waiting on a slow peer sends nothing, so nothing is acknowledged, so an ACK-only rule concludes the path is dead and marks the destination for tunnel rotation. The transfer was never broken; the churn it caused was. With nothing in flight there is nothing to be stuck behind.- Parameters:
now- clock value at the sample boundarylastProgressAt- clock value of the last observed progress, or 0 for noneobservedBps- goodput measured over the current sample windowratio- fraction of baseline below which the link is throttledsamples- completed goodput samples backing the baselinebaselineBps- EWMA baseline, 0 if not yet establishedabsFloorBps- absolute floor capping the proportional floorstallGraceMs- time with no bytes before a hard stall is reportedoutstandingData- true when unacknowledged data is waiting on the wire- Returns:
- the verdict; never null
updatedBaseline
static double updatedBaseline(double baseline, double sampleBps, int percent) Fold a completed goodput sample into the baseline EWMA.Pure. The baseline tracks capacity changes rather than freezing at the first observation, but it moves slowly enough that a transient dip does not immediately become the new reference the next window is judged against — without that, a single slow window would excuse itself.
- Parameters:
baseline- current EWMA, 0 if nonesampleBps- goodput of the sample just completedpercent- weight of the new sample, scaled by 100- Returns:
- the updated EWMA
sampleRateBps
static double sampleRateBps(long bytes, long elapsedMs) Roll the goodput sample forward, returning the rate of the window that just closed.Pure. A window that closed having seen no bytes reports 0, which the caller feeds to
updatedBaseline(double, double, int)— a quiet window must not be allowed to pull the baseline to zero and disarm the detector.- Parameters:
bytes- bytes counted in the closing windowelapsedMs- duration of the closing window- Returns:
- goodput in bytes/sec, 0 if the window was too short to measure
graduatedLossWindow
static int graduatedLossWindow(int strikes, int wsize) Graduated congestion response: the window is cut by an amount that scales with the number of CONSECUTIVE loss events since the last recovery, so a single failed packet does not collapse the connection while persistent loss still backs off.The 0.9.71+ profile ("ramp fast, decline slowly") makes every tier gentler than the classic halves/quarters — a first strike cuts only 1/8, a second another 1/4, and persistent loss settles at half — so a single loss event on a high-BDP connection does not undo an RTT of window growth, while genuinely congested paths still concede capacity.
- Parameters:
strikes- consecutive loss events (>= 1) since the last recoverywsize- current window size- Returns:
- new window size, floored at 4 (never below a usable minimum)
graduatedLossSsthresh
static int graduatedLossSsthresh(int strikes, int wsize, int bwBasedSsthresh) Slow-start threshold for a graduated loss response: never below the graduated window (so the connection can regrow to at least its post-cut capacity) and never below the bandwidth-derived estimate.- Parameters:
strikes- consecutive loss events since the last recoverywsize- current window sizebwBasedSsthresh- the bandwidth-estimate-derived threshold (>= 1)- Returns:
- slow-start threshold, floored at 1
bandwidthSsthreshAnchor
static int bandwidthSsthreshAnchor(float bwEstPacketsPerMs, int minRttMs, int factor, int floor) The bandwidth-derived slow-start threshold anchor: the packets the pipe can hold, estimated as measured bandwidth times the effective RTT times the growth factor, floored at the given minimum.The effective RTT is floored at
SS_THRESH_BW_ANCHOR_MIN_RTT(1s): a raw sub-second min-RTT would otherwise shrink the anchor below the classic fast-retransmit floor (16 packets) on a healthy path, and once the window is pinned there the low ACK rate lowers the measured bandwidth, confirming the low anchor — a self-reinforcing ~10KB/s lock.- Parameters:
bwEstPacketsPerMs- measured bandwidth in packets/ms (>= 0)minRttMs- the connection's raw min-RTT in ms (>= 0)factor- growth factor applied to the BDP estimate (>= 1)floor- minimum anchor in packets (>= 1)- Returns:
- anchor in packets, never below
floor
lossEpisodeRecovered
static boolean lossEpisodeRecovered(boolean hadCongestionMark, long ackThrough, long lastCongestionHighestUnacked, int activeResends) Whether a congestion episode's lost window has been fully recovered.Recovery is declared once the ACK stream has passed the congestion mark and no retransmits remain outstanding. The window itself need not be empty: a pipe dribbling data must not stay locked in the graduated tiers merely because it never fully drains. Requiring zero outstanding retransmits still guards against a stale ACK claiming recovery on a NACK path before the paced resends have cleared.
- Parameters:
hadCongestionMark- true when a congestion cut has ever occurredackThrough- highest contiguously-acked sequence numberlastCongestionHighestUnacked- highest unacked sequence number at the cut, -1 if noneactiveResends- retransmits still awaiting their ACK- Returns:
- true when the lost window has been recovered
decayLossStrikes
static int decayLossStrikes(int strikes, long lastLossTime, long now, long smoothedRtt) Strike decay: a single loss strike is forgiven once at least one smoothed RTT (floored at 500 ms) has passed without another strike, rolling the graduated backoff back down toward the gentle tier. One-off losses are normal on I2P, and keying the decay to elapsed time rather than ACK count means a stale lone strike cannot keep the window depressed forever, while true back-to-back loss (strikes inside the RTT window) still escalates.Pure and side-effect free for unit testing; call sites apply the result and refresh
_lastLossStrikeTimeunder_outboundPacketsLock.- Parameters:
strikes- current consecutive-loss count (>= 0)lastLossTime- wall-clock ms of the most recent strike (<= 0 if none)now- wall-clock mssmoothedRtt- the connection's smoothed RTT in ms (>= 0)- Returns:
- strikes - 1 when at least max(smoothedRtt, 500) ms have elapsed since the last strike, otherwise the current count; never below 0
stuckLifetimeExceeded
static boolean stuckLifetimeExceeded(long budgetMs, long now, long createdOn) Pure predicate: has the oldest unacked packet been in flight beyond the worst-case retransmit budget, measured from CREATION?The worst case is one transmission per RTO for the full per-packet retransmit budget, which the caller computes from the configured
maxResends * maxRtobefore calling. Anchoring the budget at the packet's creation (instead of its last transmission) gives a fixed wall-clock deadline that retransmission activity cannot extend: an established connection in resume mode keeps retransmitting a budget-exhausted head-of-line packet every RTO (refreshing its last send time) precisely so it can surge forward when the path clears, and that deadline must still hold so a genuinely dead path is closed rather than retried forever. An exact-inequality comparison keeps a live path on the budget boundary from being killed.- Parameters:
budgetMs- the worst-case in-flight budget in ms, computed by the caller as the retransmit budget times the maximum single retransmit timeout;<= 0disables the checknow- current time in ms since epochcreatedOn- creation time of the oldest packet, in ms since epoch (PacketLocal.getCreatedOn()), or <= 0 if unknown- Returns:
- true if the packet has been in flight beyond the budget and recovery is presumed dead
remoteSilentTooLong
static boolean remoteSilentTooLong(long lastReceivedOn, int inactivityTimeout, long now) Pure predicate: has the remote sent nothing at all for a full inactivity window, while unacked packets are still in flight?An ACK (or a data packet) is emitted by a live remote on receipt within the ack window, so a connection that has unacked in-flight packets AND has received zero bytes for the configured inactivity timeout can no longer be making progress in either direction: either our sends are not arriving (dead outbound leg) or the remote is gone. Holding such a connection longer cannot resume it — per the protocol the remote's own inactivity timer (default 120s,
i2p.streaming.inactivityTimeout) has already closed it, and its close is lost on the dead path back. This gives an established connection in resume mode — which otherwise retransmits a budget-exhausted head-of-line packet every RTO until the creation-anchored backstop (stuckLifetimeExceeded(long, long, long), defaulti2p.streaming.stallGiveupMs= 90s) fires — a tighter wall-clock deadline so the application gets EOF within ~one inactivity window instead of hanging on a zombie stream it can never resume.Anchoring at the last RECEIVED packet (not the last send) is deliberate: resume mode refreshes the last send time on every retransmit, so a last-send-anchored check would defer forever. A receive-anchored check only fires when the remote has truly gone silent; a healthy path that acknowledges our data — even slowly — resets
lastReceivedOnand never hits this bound. An exact-inequality comparison keeps a connection on the boundary alive.- Parameters:
lastReceivedOn- timestamp of the last packet received from the remote, in ms since epoch (Connection#packetReceived()), or <= 0 if nothing has ever been received (connect phase, which is bounded separately by the SYN give-up budget)inactivityTimeout- configured inactivity timeout in ms (> 0)now- current time in ms since epoch- Returns:
- true if the remote has been silent for the full inactivity window while unacked packets are in flight
effectiveInactivityTimeout
static int effectiveInactivityTimeout(int configured, int fallbackMs) Effective inactivity window for the remote-silence bound.The predicate requires a positive window and is silently disarmed by a
<= 0configured timeout, which would leave a dead path hanging until the creation-anchored backstop (stuckLifetimeExceeded(long, long, long)) — by default 90s resends * maxRTO ≈ 6 min. Floor the window at the protocol default (120s) so a zombie stream still dies within ~one inactivity window; the bound is deliberately never tightened below that.- Parameters:
configured- the connection's configured inactivity timeout in msfallbackMs- the floor to use when the configured window is not positive- Returns:
configuredifconfigured > 0, elsefallbackMs
hardResendBudgetExceeded
static boolean hardResendBudgetExceeded(int totalSends, int softResends, int maxResends) Pure decision: has the number of HARD send attempts (sends that took the router's reply as something to retry after, i.e. not router soft failures) exceeded the retransmit budget?A router soft failure (NO_TUNNELS / EXPIRED / LOCAL) means the message was never put on the tunnel fabric, so the attempt is not evidence of on-wire loss. Counting soft-failure-triggered resends against
maxResendsmakes a long stream abort during a tunnel handover: each soft failure immediately re-fires the retransmit timer, so ~30 router-side failures exhaust the budget and kill the connection even though the packet never left. Only genuine loss should consume the budget. The subtraction is clamped at zero so an early-tagged paced resend cannot undercount below the true hard count.- Parameters:
totalSends- total send attempts incl. resends (PacketLocal.getNumSends())softResends- of those, resends tagged as soft-failure-triggered (PacketLocal.getNumSoftResends())maxResends- configured per-packet retransmit budget (> 0)- Returns:
- true if the non-soft send attempts exceed the budget
budgetExhaustionClosesConnection
static boolean budgetExhaustionClosesConnection(boolean budgetExhausted, boolean hasForwardProgress) Pure decision: should an exhausted per-packet retransmit budget close the connection?Once a connection has made forward progress (any packet acknowledged) it may carry an in-flight download with app-level state to preserve. A single head-of-line packet that exhausts its budget is then evidence of a TEMPORARY path failure (a hole at a congested hop), not a dead path: tearing the whole stream down forces the application to reconnect and re-transfer, which is the observed "download fails and restarts" churn. Such connections keep the packet in the window and keep retransmitting — they resume — instead of closing. Only a connection that never got a single packet through (connect phase, nothing to resume) keeps the fatal close.
The
budgetExhaustedinput already excludes SYN and CLOSE packets: connect-phase give-up is decided separately (synGiveUpBudgetExceeded(int, int, int)with a distinct error), and CLOSE packets are bounded by the dedicated close-resend cap.- Parameters:
budgetExhausted- true if a non-SYN, non-CLOSE packet's hard send count has exceeded its retransmit budgethasForwardProgress- true if any packet has been acknowledged on the connection (Connection._highestAckedThrough >= 0)- Returns:
- true if the connection should close (budget exhausted AND no forward progress); false if it should resume over closing
synGiveUpBudgetExceeded
static boolean synGiveUpBudgetExceeded(int totalSends, int softResends, int maxSynSends) Pure decision: has the retransmit loop exceeded the SYN give-up budget after excluding soft-failure-triggered resends?Mirror of
hardResendBudgetExceeded(int, int, int)for the SYN-phase give-up. A SYN resent only because the router reported a soft failure (e.g. NO_LEASESET while its LeaseSet fetch is in flight) never reached the tunnel fabric, so it is not evidence of a dead path; counting it againstmaxSynSendslets a burst of router-side soft failures kill a connect in a few hundred ms with a spurious "SYN not acknowledged". Only genuine on-wire retransmits should consume the give-up budget.- Parameters:
totalSends- total SYN send attempts incl. resends (PacketLocal.getNumSends())softResends- of those, resends tagged as soft-failure-triggered (PacketLocal.getNumSoftResends())maxSynSends- per-connection SYN give-up budget (getMaxSynSends())- Returns:
- true if the non-soft SYN send attempts exceed the budget
shouldPaceRetx
static boolean shouldPaceRetx(int burstCount, int nResends) Pure decision: should this packet's retransmit be paced through the paced queue rather than sent directly?The first
IMMEDIATE_RETX_BURSTpackets of a timer fire are sent directly to avoid flooding the I2CP queue with a single-timer-fire burst. Beyond that, pacing spreads the load. Packets already transmittedMAX_PACED_RETXtimes or more are recovery-critical and always sent directly so they cannot starve behind a stale pacing rate in the paced queue (hard drain deadline).- Parameters:
burstCount- number of direct sends already made in this timer firenResends- transmissions this packet has undergone- Returns:
- true if the retransmit should go through pacing
windowAdjusted
void windowAdjusted()Notify all threads waiting inpacketSendChoke(long). Also update pacing rate since window size may have changed.ackImmediately
void ackImmediately()Ack immediately.sendAvailable
void sendAvailable()Flush any data that we can. Non-blocking.scheduleSoftFailureRetransmit
void scheduleSoftFailureRetransmit()Trigger immediate retransmission of unacked packets after a soft failure (e.g., tunnel expiry, no tunnels). This schedules the retransmit event with zero delay to retry sending on a new tunnel without waiting for RTO.Immediate reschedules are rate-limited: when the router soft-fails every message (STATUS_SEND_FAILURE_NO_LEASESET while the LeaseSet fetch for a fresh destination is still in flight), each report used to cancel the retransmit timer and fire IMMEDIATELY. Back-to-back reports arrived at the I2CP round-trip rate (~10ms) and each immediate pass bumped the SYN's send count once, exhausting the SYN give-up budget (getMaxSynSends()) in ~200ms and killing the connect with a spurious "SYN not acknowledged" before the LeaseSet could be fetched. During the SYN phase the minimum gap is the SYN retransmit interval, so retries are interval-spaced exactly as the give-up budget math assumes; for established connections a small fixed floor prevents any spin while still hopping a replacement tunnel far sooner than the (possibly doubled) RTO.
shouldRateLimitSoftRetransmit
static boolean shouldRateLimitSoftRetransmit(long now, long lastSoftFailRetransmit, long minSpacingMs) Pure decision: is this soft-failure report too close to the last permitted immediate retransmit to fire another now?Derived from
scheduleSoftFailureRetransmit(); extracted for testability. A report at exactlyminSpacingMsafter the last is allowed, mirroring the strict-inequality pacing of the retransmit timer.- Parameters:
now- wall-clock time of this soft-failure report (ms)lastSoftFailRetransmit- wall-clock time of the last permitted immediate retransmit (ms), 0 if none yet this connectionminSpacingMs- minimum gap between immediate retransmits (ms)- Returns:
- true if the retransmit should be deferred to the existing timer
sendPacket
This sends all 'normal' packets (acks and data) for the first time. Retransmits are done in ResendPacketEvent below. Resets, pings, and pongs are done elsewhere in this class, or inConnectionManageror ConnectionHandler.- Parameters:
packet- the ACK or data packet to send for the first time; null is ignored
ackPackets
Process the acks and nacks received in a packet.Note: the returned list is a reused, internally-owned buffer (
_ackedList) that is cleared at the start of the nextackPackets()call. Callers MUST fully consume it before the next ack is processed. This is safe because acks are serialized on the receive thread; do not retain a reference across calls.- Parameters:
ackThrough- the highest packet sequence number the peer has received contiguously; anything lower counts as ACKed unless NACKednacks- sequence numbers explicitly reported as lost, or null or empty if the peer sent none- Returns:
- List of packets acked for the first time (empty if none); a shared mutable buffer, not a fresh copy
eventOccurred
void eventOccurred()Notify the scheduler that an event occurred on this connection.notifyCloseSent
public void notifyCloseSent()Called by CPH when a CLOSE packet is sent. Idempotent: only the first call sets the timestamp.closeReceived
public void closeReceived()Notify that a close was received. Called by CPH. May be called multiple times.notifyLastPacketAcked
public void notifyLastPacketAcked()Notify that a close that we sent, and all previous packets, were acked. Called by CPH. Only call this once.resetReceived
public void resetReceived()Notify that a reset was received. May be called multiple times.getResetReceived
public boolean getResetReceived()Check if a reset has been received on this connection.- Returns:
- true if a reset was received
getResetReceivedOn
public long getResetReceivedOn()Timestamp when a reset was received.- Returns:
- 0 if not received
isInbound
public boolean isInbound()Check if this is an inbound connection.- Returns:
- true if inbound
getIsConnected
public boolean getIsConnected()Always true at the start, even if we haven't gotten a reply on an outbound connection. Only set to false on disconnect. For outbound, usegetHighestAckedThrough()>= 0 also, to determine if the connection is up.In general, this is true until either: - CLOSE received and CLOSE sent and our CLOSE is acked - RESET received or sent - closed on the socket side.
- Returns:
- the is connected
getHardDisconnected
public boolean getHardDisconnected()Check if this connection has been hard-disconnected (via RESET).- Returns:
- true if hard-disconnected
getResetSent
public boolean getResetSent()Check if a reset has been sent on this connection.- Returns:
- true if a reset was sent
getResetSentOn
public long getResetSentOn()Timestamp when a reset was sent.- Returns:
- 0 if not sent
getDisconnectScheduledOn
public long getDisconnectScheduledOn()Timestamp when the disconnect was scheduled.- Returns:
- 0 if not scheduled
disconnect
public void disconnect(boolean cleanDisconnect) Must be called when we are done with this connection. Enters TIME-WAIT if necessary, and removes from connection manager. May be called multiple times. This closes the socket side. In normal operation, this is called when a CLOSE has been received, AND a CLOSE has been sent, AND EITHER: received close before sent close AND our CLOSE has been acked OR received close after sent close.- Parameters:
cleanDisconnect- if true, normal close; if false, send a RESET
disconnect
public void disconnect(boolean cleanDisconnect, boolean removeFromConMgr) Must be called when we are done with this connection. May be called multiple times. This closes the socket side. In normal operation, this is called when a CLOSE has been received, AND a CLOSE has been sent, AND EITHER: received close before sent close AND our CLOSE has been acked OR received close after sent close.- Parameters:
cleanDisconnect- if true, normal close; if false, send a RESETremoveFromConMgr- if true, enters TIME-WAIT if necessary. if false, MUST calldisconnectComplete()later. Should always be true unless called from ConnectionManager.
disconnectComplete
public void disconnectComplete()Must be called when we are done with this connection. Final disconnect. Remove from conn manager. May be called multiple times.scheduleConnectionEvent
public void scheduleConnectionEvent(long msToWait) Called fromSchedulerImpl.- Parameters:
msToWait- wait in ms before the ConEvent releases its waiters
getAckDupEvent
Connection.AckDupEvent getAckDupEvent()Schedule an event on our timer. The event should use the no-arg constructor; the pool is set here.- Returns:
- the reusable AckDupEvent for duplicate ACK scheduling
getRemotePeer
Destination of the remote peer.- Returns:
- peer Destination or null if unset
setRemotePeer
Remote peer of this connection, non-null.- Parameters:
peer- non-null
getRemoteSPK
The key to verify signatures with. The transient SPK if previously received, elsegetRemotePeer().getSigningPublicKey() if previously received, else null.- Returns:
- peer Destination or null if unset
setRemoteTransientSPK
Transient signing public key of the remote peer.- Parameters:
transientSPK- null ok
getSendStreamId
public long getSendStreamId()What stream do we send data to the peer on?- Returns:
- non-global stream sending ID, or 0 if unknown
setSendStreamId
public void setSendStreamId(long id) Stream ID that we send data on.- Parameters:
id- 0 to 0xffffffff- Throws:
IllegalStateException- if already set to nonzero
getReceiveStreamId
public long getReceiveStreamId()The stream ID of a peer connection that sends data to us, or zero if unknown.- Returns:
- receive stream ID, or 0 if unknown
claimSlotRelease
boolean claimSlotRelease()Claim this connection's slot release, once per generation. The winner (first CAS false→true) owns the budget release; every other teardown path — a disconnectAllHard() sweep racing disconnectComplete(), a failed bind's catch racing a later teardown — sees true and must skip its release, or the dest's stream count is decremented for slots it never took.- Returns:
- true if the caller now owns the one release for this generation
resetSlotRelease
void resetSlotRelease()Re-arm the slot-release claim for a new generation, called byConnectionManager.assignReceiveStreamId(Connection, Hash)when a pooled connection binds a fresh reservation token: the next teardown of this object must be able to claim again.setReceiveStreamId
public void setReceiveStreamId(long id) Stream ID that the peer sends data on.- Parameters:
id- 0 to 0xffffffff- Throws:
IllegalStateException- if already set to nonzero
getLastSendTime
public long getLastSendTime()When did we last send anything to the peer?- Returns:
- Last time we sent data
getLastSendId
public long getLastSendId()What was the last packet Id sent to the peer?- Returns:
- The last sent packet ID
getOptions
Retrieve the current ConnectionOptions.- Returns:
- the current ConnectionOptions, non-null
setOptions
ConnectionOptions for this connection.- Parameters:
opts- ConnectionOptions non-null
getConnectionManager
The ConnectionManager that created this connection.- Returns:
- the owning connection manager
getSession
I2P session for this connection.- Returns:
- the I2PSession this connection sends and receives over
getSocket
Socket associated with this connection.- Returns:
- the socket, or null if not yet set
setSocket
Socket associated with this connection.- Parameters:
socket- the socket the connection will read and write on once it is established
getPort
public int getPort()The remote port.- Returns:
- Default I2PSession.PORT_UNSPECIFIED (0) or PORT_ANY (0)
getLocalPort
public int getLocalPort()Local port of this connection.- Returns:
- Default I2PSession.PORT_UNSPECIFIED (0) or PORT_ANY (0)
getConnectionError
Connection error message, if any.- Returns:
- error message, or null if no error
setConnectionError
Connection error message.- Parameters:
err- the error message
getLifetime
public long getLifetime()Lifetime of this connection in milliseconds.- Returns:
- connection lifetime in ms
getPacketHandler
Packet handler for this connection.- Returns:
- the packet handler
getLifetimeBytesSent
public long getLifetimeBytesSent()Total bytes sent on this connection.- Returns:
- lifetime bytes sent
getLifetimeBytesReceived
public long getLifetimeBytesReceived()Total bytes received on this connection.- Returns:
- lifetime bytes received
getLifetimeDupMessagesSent
public long getLifetimeDupMessagesSent()Total duplicate messages sent on this connection.- Returns:
- lifetime duplicate messages sent
getLifetimeDupBytesSent
public long getLifetimeDupBytesSent()Total duplicate bytes sent on this connection.- Returns:
- lifetime duplicate bytes sent
getLifetimeDupMessagesReceived
public long getLifetimeDupMessagesReceived()Total duplicate messages received on this connection.- Returns:
- lifetime duplicate messages received
incrementBytesSent
public void incrementBytesSent(int bytes) Increment the lifetime bytes sent counter.- Parameters:
bytes- number of bytes to add
incrementDupMessagesSent
public void incrementDupMessagesSent(int msgs) Increment the lifetime duplicate messages sent counter.- Parameters:
msgs- number of duplicate messages to add
incrementDupBytesSent
public void incrementDupBytesSent(int bytes) Increment the lifetime duplicate bytes sent counter.- Parameters:
bytes- number of duplicate bytes to add
incrementBytesReceived
public void incrementBytesReceived(int bytes) Increment the lifetime bytes received counter.- Parameters:
bytes- number of bytes to add
incrementDupMessagesReceived
public void incrementDupMessagesReceived(int msgs) Increment the lifetime duplicate messages received counter.- Parameters:
msgs- number of duplicate messages to add
getNextSendTime
public long getNextSendTime()Time when the scheduler next want to send a packet, or -1 if never. This should be set when we want to send on timeout, for instance, or want to delay an ACK.- Returns:
- the next time the scheduler will want to send a packet, or -1 if never.
setNextSendTime
public void setNextSendTime(long when) If the next send time is currently >= 0 (i.e. not "never"), this may make the next time sooner but will not make it later. If the next send time is currently < 0 (i.e. "never"), this will set it to the time specified, but not later than options.getSendAckDelay() from now (1000 ms).- Parameters:
when- the desired send time in ms since the epoch, or < 0 for "never"
setChoking
public void setChoking(boolean on) Choking state toward the other side. If on is true or the value has changed, this will call ackImmediately().- Parameters:
on- true for choking
setChoked
public void setChoked(boolean on) Choked state set by the other side.- Parameters:
on- true for choked
isChoked
public boolean isChoked()Whether the other side is choking us.- Returns:
- true if the remote has advertised a zero window
restoredWindowOnUnchoke
static int restoredWindowOnUnchoke(int saved, int ceiling) The window to restore when the remote unchokes us.Restores the pre-choke window and nothing more, capped at the current per-stream ceiling so a window saved under a more generous ceiling cannot jump the queue past what the pipe and heap allow now.
The window deliberately does not consider
ssthresh. Folding it in (the oldmax(saved, ssthresh)) made every unchoke restore the ceiling instead of the demonstrated window, because ssthresh starts atmaxSlowStartWindowand is an asymptotic growth target rather than a capacity measurement. A connection whose window had genuinely collapsed to a handful of packets would be reinflated to the full ceiling on every unchoke, and since a remote only chokes when it is overloaded, that fixed burst fed the loss that caused the next cut.- Parameters:
saved- the window captured when the choke began, always positive at the call siteceiling- the current per-stream window ceiling, in messages- Returns:
- the window to restore, in
[1, ceiling]
getAckedPackets
public long getAckedPackets()How many packets have we sent and the other side has ACKed?- Returns:
- Count of how many packets ACKed.
getCreatedOn
public long getCreatedOn()Timestamp when this connection was created.- Returns:
- router clock time in milliseconds at construction
getCloseSentOn
public long getCloseSentOn()Timestamp when a close was sent.- Returns:
- 0 if not sent
getCloseReceivedOn
public long getCloseReceivedOn()Timestamp when a close was received.- Returns:
- 0 if not received
incrementUnackedPacketsReceived
public void incrementUnackedPacketsReceived()Increment the count of unacked packets received.getUnackedPacketsReceived
public int getUnackedPacketsReceived()Count of unacked packets received.- Returns:
- number of unacked packets received
getUnackedPacketsSent
public int getUnackedPacketsSent()How many packets have we sent but not yet received an ACK for?- Returns:
- Count of packets in-flight.
getWindowLock
ForConnectionPacketHandler.adjustWindow(Connection, boolean, long, int, int, boolean).- Returns:
- the monitor guarding the outbound window map
getCongestionWindowEnd
public long getCongestionWindowEnd()Congestion window end sequence number.- Returns:
- the congestion window end
setCongestionWindowEnd
public void setCongestionWindowEnd(long endMsg) Congestion window end sequence number.- Parameters:
endMsg- the new congestion window end
getHighestAckedThrough
public long getHighestAckedThrough()Highest outbound packet we have received an ack for.- Returns:
- the highest outbound packet we have received an ack for
getLastActivityOn
public long getLastActivityOn()Timestamp of the last send or receive activity.- Returns:
- the later of last send time and last receive time
packetReceived
void packetReceived()Called when a packet is received on this connection. Updates the last received timestamp and resets the activity timer.waitForConnect
void waitForConnect()Wait for the connection to be established, with the option's connectTimeout.waitForConnect
void waitForConnect(int timeoutMs) Wait for the connection to be established, but no longer than timeoutMs. If the connection fails or the timeout is exceeded, sets _connectionError.- Parameters:
timeoutMs- max wait in ms; if <= 0 uses the connection option's connectTimeout
getInputStream
Input stream that the local peer receives data on.- Returns:
- the inbound message stream, non-null
getOutputStream
Output stream that the local peer sends data to the remote peer on.- Returns:
- the outbound message stream, non-null
toString
newResendPacketEvent
A new ResendPacketEvent from the pool or a fresh instance.- Parameters:
packet- the packet the event will retransmit- Returns:
- the reused-or-new event, already bound to that packet