@decentnetwork/peer 0.1.70 → 0.1.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compat/filetransfer.js +35 -12
- package/package.json +1 -1
|
@@ -30,15 +30,23 @@ export const MAX_CONCURRENT_FILE_PIPES = 256;
|
|
|
30
30
|
export const MAX_FILE_DATA_SIZE = 1367;
|
|
31
31
|
export const FILECONTROL = { ACCEPT: 0, PAUSE: 1, KILL: 2, SEEK: 3, ACK: 4 };
|
|
32
32
|
export const FILEKIND = { DATA: 0, AVATAR: 1 };
|
|
33
|
-
// Reliability tuning. We
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
// Reliability tuning. We RAMP an in-flight window (AIMD-lite) rather than pin it:
|
|
34
|
+
// - RANDOM single-packet loss (dup-ack with a gap) is NOT congestion — fast-
|
|
35
|
+
// retransmit the hole and DON'T shrink the window, so lossy-but-fat paths
|
|
36
|
+
// (Wi-Fi / long-haul jitter) keep full throughput (the original intent).
|
|
37
|
+
// - A SUSTAINED stall (no ack progress for ~RTT) IS congestion — halve the
|
|
38
|
+
// window so we stop flooding the path. This is what keeps the *session* alive
|
|
39
|
+
// during a big transfer over a thin/relayed hop: without it, a fixed 1 MB
|
|
40
|
+
// window blasted go-back-N saturates the path, starves the net_crypto
|
|
41
|
+
// keepalive, and the whole session (ping included) times out. On a good path
|
|
42
|
+
// the window ramps back to WINDOW_CHUNKS within a few RTTs → same speed.
|
|
43
|
+
// The 4 MB UDP socket buffer absorbs bursts; the receiver buffers by offset.
|
|
44
|
+
const WINDOW_CHUNKS = 768; // ~1 MB — the window CAP (fills a 200ms-RTT path at ~5 MB/s)
|
|
45
|
+
const CWND_INIT_CHUNKS = 96; // ~131 KB start — ramps to the cap fast on a clean path
|
|
46
|
+
const CWND_MIN_CHUNKS = 24; // ~33 KB floor — still enough to keep a thin path busy
|
|
47
|
+
const CWND_GROW_CHUNKS = 48; // additive increase per ack-progress event
|
|
48
|
+
const WATCHDOG_MS = 150; // stall poll cadence / base no-progress patience
|
|
49
|
+
const WATCHDOG_MAX_MS = 2400; // cap the RTT-adaptive stall patience (high-RTT relay paths)
|
|
42
50
|
const FAST_RT_MIN_MS = 40; // min gap between fast-retransmits of the same hole (≈1 RTT)
|
|
43
51
|
const ACK_THROTTLE_MS = 15; // coalesce receiver acks (fast enough to drive fast-retransmit)
|
|
44
52
|
const REACK_MS = 200; // receiver keep-alive re-ack cadence
|
|
@@ -140,6 +148,7 @@ export class FileTransferManager {
|
|
|
140
148
|
fileNumber, fileId, name: opts.name, data, size: data.length,
|
|
141
149
|
acked: 0, nextSend: 0, pumping: false, started: false, req, startMs: nowMs(),
|
|
142
150
|
lastProgressAcked: 0, lastAckAdvanceMs: nowMs(), lastResendMs: 0,
|
|
151
|
+
cwnd: CWND_INIT_CHUNKS, stalls: 0,
|
|
143
152
|
};
|
|
144
153
|
this.#map(this.#sending, friendId).set(fileNumber, st);
|
|
145
154
|
void this.send(friendId, PACKET_ID_FILE_SENDREQUEST, req).catch(() => undefined);
|
|
@@ -360,9 +369,12 @@ export class FileTransferManager {
|
|
|
360
369
|
const ackedOffset = readU32be(data, 0);
|
|
361
370
|
const maxRecv = data.length >= 8 ? readU32be(data, 4) : ackedOffset;
|
|
362
371
|
if (ackedOffset > st.acked) {
|
|
363
|
-
// Progress.
|
|
372
|
+
// Progress. The path is delivering → additively re-open the window (up to
|
|
373
|
+
// the cap) and clear the stall streak (patience shrinks back to the base).
|
|
364
374
|
st.acked = Math.min(ackedOffset, st.size);
|
|
365
375
|
st.lastAckAdvanceMs = nowMs();
|
|
376
|
+
st.cwnd = Math.min(WINDOW_CHUNKS, st.cwnd + CWND_GROW_CHUNKS);
|
|
377
|
+
st.stalls = 0;
|
|
366
378
|
if (st.acked - st.lastProgressAcked >= 256 * 1024 || st.acked >= st.size) {
|
|
367
379
|
st.lastProgressAcked = st.acked;
|
|
368
380
|
this.emit("file-progress", { friendId, fileId: hex(st.fileId), received: st.acked, total: st.size, sending: true });
|
|
@@ -402,7 +414,12 @@ export class FileTransferManager {
|
|
|
402
414
|
}
|
|
403
415
|
if (st.acked >= st.size)
|
|
404
416
|
return;
|
|
405
|
-
|
|
417
|
+
// RTT-adaptive patience: after consecutive stalls, wait longer before
|
|
418
|
+
// deciding the path stalled — so a high-RTT relay hop doesn't trigger a
|
|
419
|
+
// spurious go-back-N re-blast before an ACK could even have returned
|
|
420
|
+
// (that spurious re-blast is exactly what collapses a thin path).
|
|
421
|
+
const patience = Math.min(WATCHDOG_MAX_MS, WATCHDOG_MS << Math.min(st.stalls, 4));
|
|
422
|
+
if (nowMs() - st.lastAckAdvanceMs < patience) {
|
|
406
423
|
stalledSinceMs = nowMs();
|
|
407
424
|
return;
|
|
408
425
|
}
|
|
@@ -411,6 +428,12 @@ export class FileTransferManager {
|
|
|
411
428
|
this.emit("file-cancel", { friendId, fileId: hex(st.fileId), sending: true, reason: "timeout" });
|
|
412
429
|
return;
|
|
413
430
|
}
|
|
431
|
+
// Sustained stall = congestion/path signal → multiplicatively cut the
|
|
432
|
+
// window so we STOP flooding. This frees the path for the net_crypto
|
|
433
|
+
// keepalive + acks, keeping the session (and ping) alive; the transfer
|
|
434
|
+
// then self-clocks back up as acks resume.
|
|
435
|
+
st.stalls++;
|
|
436
|
+
st.cwnd = Math.max(CWND_MIN_CHUNKS, Math.floor(st.cwnd / 2));
|
|
414
437
|
st.lastAckAdvanceMs = nowMs();
|
|
415
438
|
st.lastResendMs = nowMs();
|
|
416
439
|
if (st.nextSend > st.acked)
|
|
@@ -425,7 +448,7 @@ export class FileTransferManager {
|
|
|
425
448
|
st.pumping = true;
|
|
426
449
|
try {
|
|
427
450
|
while (this.#map(this.#sending, friendId).has(st.fileNumber) && st.acked < st.size) {
|
|
428
|
-
const windowEnd = Math.min(st.acked +
|
|
451
|
+
const windowEnd = Math.min(st.acked + st.cwnd * MAX_FILE_DATA_SIZE, st.size);
|
|
429
452
|
if (st.nextSend >= windowEnd)
|
|
430
453
|
break; // window full / all sent — wait for acks
|
|
431
454
|
const off = st.nextSend;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.71",
|
|
4
4
|
"description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|