5.1 KiB
ZTorrent — TODO & Контекст улучшений
Цель: довести ztorrent до уровня qBittorrent по функциональности и скорости. Ведётся автоматически — отражает актуальное состояние реализации.
Архитектура проекта
internal/
torrent/
engine.go — основной движок: загрузка торрента, воркеры, координация
peerwire.go — BitTorrent wire protocol: handshake, download piece, messages
piecescheduler.go — выбор кусков (rarest-first), endgame mode
debug.go — debugf helper
tracker/
tracker.go — HTTP + UDP announce
torrentfile/
torrentfile.go — парсинг .torrent (bencode)
app/
controller.go — тонкая обёртка над Engine для UI
ui/
ui.go — Bubble Tea TUI (welcome, loading, dashboard)
styles.go — цветовая палитра
P0 — Критичные ✅ ГОТОВО
- Resume download —
createOrOpenPartFile()без O_TRUNC + SHA-1 верификация - Keep-alive —
keepaliveLoop()горутина, 90с,msgCancel=8 - Endgame mode —
pieceDonebroadcast +SendCancel()при дублировании
P1 — Производительность ✅ ГОТОВО
- Adaptive pipeline — depth 4–64, пересчёт каждые 4 блока по RTT+BW
- IPv6 compact peers —
parsePeers6()в tracker.go - Rate limiting —
rateLimitedConn,SetDownloadLimit/SetUploadLimit - Bitmap resume data —
.part.bitmapJSON,asyncSaveBitmap()в heartbeat
Баги ✅ ПОФИКШЕНО
-
announceпустой — BEP 12 fallback: берём первый URL изannounce-list>torrentfile.go→primaryAnnouncefallback вOpen() -
peerBackoffO(n) → O(1) черезpeerIdx map[string]int> ДобавленEngine.peerIdx,peerByKey()helper > Все peer-методы:setPeerState,setPeerError,peerBackoff,recordPieceComplete -
Скорость без EWMA → сглаживание α=0.3 в
updateSpeedLocked()>downloadSpeed = 0.3*sample + 0.7*prev -
Reannounce блокирует heartbeat → запускается в отдельной горутине >
heartbeatTicker.C→go func() { refreshPeersFromTrackers() }()> Таймаут уменьшен с 20с до 15с
Фича ✅ РЕАЛИЗОВАНО
- Incoming connections — TCP listener на :6881
>
listenIncoming()+handleIncoming()в engine.go > Handshake верификация (info_hash), регистрация вpeerIdx> Входящие пиры подхватываютсяstartWorkersна следующем heartbeat тике
P2 — Долгосрочные (не начато)
-
DHT (BEP 5) — Kademlia DHT для поиска пиров без трекера > Новый пакет
internal/dht/— реализовано! -
Магнет-ссылки (BEP 9) —
magnet:?xt=..., ut_metadata extension > Новый пакетinternal/magnet/— средняя сложность -
Seeding / upload (tit-for-tat) — раздача после завершения >
peerwire.go→ обработкаmsgRequest, отправкаmsgPiece -
PEX (BEP 11) — Peer Exchange между пирами > Extension handshake +
ut_pexmessage
Технические решения
peerIdx — O(1) peer lookup
Engine.peers []PeerStatus // слайс со всеми пирами
Engine.peerIdx map[string]int // "host:port" → индекс в слайсе
buildPeerIdx(peers) // строится при LoadTorrent и reannounce
peerByKey(key) *PeerStatus // O(1) под e.mu
EWMA сглаживание скорости
updateSpeedLocked():
sample = delta_bytes / elapsed_sec
speed = 0.3*sample + 0.7*prev_speed // α=0.3
Incoming connections flow
runDownload()
└── go listenIncoming(ctx, tf)
└── net.Listen(":6881")
└── for { conn = Accept() → go handleIncoming(conn) }
├── ReadFull → verify wireProtocol + infoHash
├── Write reply handshake
└── register peer in peerIdx
→ подхватывается startWorkers на следующем heartbeat
Resume flow
createOrOpenPartFile():
├── если .part существует + .bitmap → verifyPieces() (быстро)
├── если .part существует, нет bitmap → verifyAllPieces() (SHA-1 scan)
└── если .part нет → создаём новый, Truncate(size)
heartbeat:
asyncSaveBitmap() → .part.bitmap
завершение:
os.Remove(.part) + os.Remove(.part.bitmap)