From 4f138ac630e9b6869db6fb1bc21fd72d171a45eb Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 18 Aug 2026 20:53:38 -0400 Subject: [PATCH] collect and return unload/download rate stats --- libi2pd_client/Torrents.cpp | 59 +++++++++++++++++++++++++++++++++- libi2pd_client/Torrents.h | 18 ++++++++++- libi2pd_client/TorrentsRPC.cpp | 10 +++--- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index 399401c9..2e936503 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -305,6 +305,7 @@ namespace torrents m_Length (0), m_PieceLength (0), m_Interval (MIN_TRACKER_REQUESTS_INTERVAL), m_NextTrackerRequestTime (0), m_IsComplete (false), m_Uploaded (0) { + ResetStats (); ParseDictionary (buf, [this](std::string_view key, std::string_view buf)->size_t { if (key == "announce") @@ -662,7 +663,9 @@ namespace torrents i2p::client::I2PServiceHandler (owner), m_Stream (stream), m_ReceiveBufferOffset (0), m_IsHandshakeSent (false), m_IsEstablished (false), m_IsChoked (true), m_IsRemoteChoked (true), m_IsInterested (false), m_IsRemoteInterested (false), m_LastReceiveTime (0), m_LastSendTime (0), - m_NumRequests (0), m_NumPieces (0), m_LastRequestedPieceIndex (-1) + m_NumRequests (0), m_NumPieces (0), m_LastRequestedPieceIndex (-1), + m_DownloadRate (0), m_UploadRate (0), m_LastBlockDownloadTimestamp (0), + m_LastBlockUploadTimestamp (0), m_ReceivedSinceLastTimestamp (0) { } @@ -1124,6 +1127,24 @@ namespace torrents } if (m_NumRequests > 0) m_NumRequests--; RequestNextBlocks (); + // update stats + auto ts = i2p::util::GetMonotonicMilliseconds (); + if (m_LastBlockDownloadTimestamp) + { + m_ReceivedSinceLastTimestamp += REQUEST_BLOCK_SIZE; + auto delta = ts - m_LastBlockDownloadTimestamp; + if (delta >= BANDWIDTH_RATE_SAMPLING_INTERVAL) + { + if (m_DownloadRate) + m_DownloadRate = (m_DownloadRate + m_ReceivedSinceLastTimestamp*1000/delta)/2; + else + m_DownloadRate = m_ReceivedSinceLastTimestamp*1000/delta; + m_LastBlockDownloadTimestamp = ts; + m_ReceivedSinceLastTimestamp = 0; + } + } + else + m_LastBlockDownloadTimestamp = ts; } void PeerConnection::SendPieceMsg (uint32_t index, uint32_t offset, const uint8_t * data, size_t len) @@ -1154,6 +1175,20 @@ namespace torrents s->m_IsRemoteChoked = false; s->SendUnchokeMsg (); } + // update status + auto ts = i2p::util::GetMonotonicMilliseconds (); + if (s->m_LastBlockUploadTimestamp) + { + auto delta = ts - s->m_LastBlockUploadTimestamp; + if (delta) + { + if (s->m_UploadRate) + s->m_UploadRate = (s->m_UploadRate + REQUEST_BLOCK_SIZE*1000/delta)/2; + else + s->m_UploadRate = REQUEST_BLOCK_SIZE*1000/delta; + } + } + s->m_LastBlockUploadTimestamp = ts; } else s->Terminate (); @@ -1910,6 +1945,7 @@ namespace torrents { auto ts = i2p::util::GetMonotonicSeconds (); for (auto it: m_Torrents) + { if (!it.second->IsComplete ()) { if (it.second->UpdateStatus (ts)) @@ -1917,6 +1953,8 @@ namespace torrents else UpdatePeersPerPiece (it.second); } + } + UpdateStats (); ScheduleStatusUpdate (); } } @@ -1990,5 +2028,24 @@ namespace torrents } }); } + + void TorrentsTunnel::UpdateStats () + { + for (auto it: m_Torrents) + it.second->ResetStats (); + IterateHandlers ([](std::shared_ptr handler) mutable + { + if (handler) + { + auto conn = std::static_pointer_cast(handler); + auto torrent = conn->GetTorrent (); + if (torrent) + { + torrent->SetDownloadRate (torrent->GetDownloadRate () + conn->GetDownloadRate ()); + torrent->SetUploadRate (torrent->GetUploadRate () + conn->GetUploadRate ()); + } + } + }); + } } } diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 8fcead3c..e33b1814 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -53,6 +53,7 @@ namespace torrents constexpr int PIECE_INACTIVITY_TIMEOUT = 60; // in seconds constexpr int TORRENTS_STATUS_UPDATE_INTERVAL = 25; // in seconds constexpr int HANDSHAKE_RECEIVE_TIMEOUT = 20; // in seconds + constexpr int BANDWIDTH_RATE_SAMPLING_INTERVAL = 20; // in milliseconds constexpr size_t HANDSHAKE_MSG_LENGTH = 68; constexpr size_t INTERESTED_MSG_LENGTH = 5; @@ -196,6 +197,12 @@ namespace torrents void ApplyPeerRemoteBitfield (const boost::dynamic_bitset<>& peerRemoteBitfield); bool HasIncompletePieces (const boost::dynamic_bitset<>& peerRemoteBitfield) const; // if remote bitfie;d has incomplete pieces + void ResetStats () { m_DownloadRate = 0; m_UploadRate = 0; } + uint64_t GetDownloadRate () const { return m_DownloadRate; } + void SetDownloadRate (uint64_t downloadRate) { m_DownloadRate = downloadRate; } + uint64_t GetUploadRate () const { return m_UploadRate; } + void SetUploadRate (uint64_t uploadRate) { m_UploadRate = uploadRate; } + private: size_t ParsePieces (std::string_view buf); @@ -216,6 +223,8 @@ namespace torrents bool m_IsComplete; std::list > m_Files; // list of (path, length) size_t m_Uploaded; + // stats + uint64_t m_DownloadRate, m_UploadRate; // B/sec }; class TorrentsTunnel; @@ -247,6 +256,9 @@ namespace torrents int GetLastRequestedPieceIndex () const { return m_LastRequestedPieceIndex; } const boost::dynamic_bitset<>& GetRemoteBitfield () const { return m_RemoteBitfield;} ; + uint64_t GetDownloadRate () const { return m_DownloadRate; } + uint64_t GetUploadRate () const { return m_UploadRate; } + private: void Terminate (); @@ -281,7 +293,6 @@ namespace torrents bool RequestNextBlock (); void RequestNextBlock (uint32_t index, uint32_t offset, uint32_t len); bool RequestNextBlocks (); - bool SendRequestedBlock (const RequestedBlock& requestedBlock); private: @@ -299,6 +310,10 @@ namespace torrents std::list m_IncomingRequestsQueue; int m_LastRequestedPieceIndex; std::unique_ptr m_HandshakeReceiveTimer; + // stats + uint64_t m_DownloadRate, m_UploadRate; // B/sec + uint64_t m_LastBlockDownloadTimestamp, m_LastBlockUploadTimestamp; // monotonic milliseconds + size_t m_ReceivedSinceLastTimestamp; // bytes }; class TorrentsTunnel final: public i2p::client::I2PService @@ -365,6 +380,7 @@ namespace torrents void ConnectToPeer (std::shared_ptr torrent, const i2p::data::IdentHash& peer); size_t ConnectToPeers (std::shared_ptr torrent); void UpdatePeersPerPiece (std::shared_ptr torrent); + void UpdateStats (); private: diff --git a/libi2pd_client/TorrentsRPC.cpp b/libi2pd_client/TorrentsRPC.cpp index 2925098b..7be08eef 100644 --- a/libi2pd_client/TorrentsRPC.cpp +++ b/libi2pd_client/TorrentsRPC.cpp @@ -152,9 +152,7 @@ namespace torrents { auto arguments = jsonRequest.at ("arguments").as_object (); bool deleteFiles = false; - if (arguments.contains ("delete_local_data")) // for transmission >= 4.1 - deleteFiles = arguments.at ("delete_local_data").as_bool (); - else if (arguments.contains ("delete-local-data")) // for transmission < 4.1 + if (arguments.contains ("delete-local-data")) deleteFiles = arguments.at ("delete-local-data").as_bool (); std::vector torrentIds; if (arguments.contains ("ids")) @@ -204,9 +202,9 @@ namespace torrents t["is_finished"] = torrent->IsComplete (); t["size_when_done"] = torrent->GetLength (); t["left_until_done"] = torrent->GetLeft (); - t["eta"] = 1; // TODO - t["rate_download"] = 1000; // B/s TODO: - t["rate_upload"] = 0; // B/s TODO: + t["eta"] = 600; // TODO + t["rate_download"] = torrent->GetDownloadRate (); + t["rate_upload"] = torrent->GetUploadRate (); t["upload_ratio"] = 1; // TODO: if (torrentsRequested) {