From 7f75a1ac77fc5cca333474de0f0f144d84336eff Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Mon, 28 Oct 2024 01:12:49 +0200 Subject: [PATCH] implement synchronized Tracker structure --- .../mykola2312/retracker/tracker/Torrent.java | 2 +- .../mykola2312/retracker/tracker/Tracker.java | 48 ++++++++++++ .../retracker/tracker/TorrentTest.java | 2 +- .../retracker/tracker/TrackerTest.java | 74 +++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) rename src/{test => main}/java/com/mykola2312/retracker/tracker/Torrent.java (92%) create mode 100644 src/main/java/com/mykola2312/retracker/tracker/Tracker.java create mode 100644 src/test/java/com/mykola2312/retracker/tracker/TrackerTest.java diff --git a/src/test/java/com/mykola2312/retracker/tracker/Torrent.java b/src/main/java/com/mykola2312/retracker/tracker/Torrent.java similarity index 92% rename from src/test/java/com/mykola2312/retracker/tracker/Torrent.java rename to src/main/java/com/mykola2312/retracker/tracker/Torrent.java index 7f1bc75..3930dcc 100644 --- a/src/test/java/com/mykola2312/retracker/tracker/Torrent.java +++ b/src/main/java/com/mykola2312/retracker/tracker/Torrent.java @@ -31,7 +31,7 @@ public class Torrent { } } - public > void updatePeers(T src) { + public > void putPeers(T src) { for (Peer peer : src) { peers.put(peer); } diff --git a/src/main/java/com/mykola2312/retracker/tracker/Tracker.java b/src/main/java/com/mykola2312/retracker/tracker/Tracker.java new file mode 100644 index 0000000..9c49a74 --- /dev/null +++ b/src/main/java/com/mykola2312/retracker/tracker/Tracker.java @@ -0,0 +1,48 @@ +package com.mykola2312.retracker.tracker; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +/* Tracker meant to be called from multiple running threads, + * so thread-safety ensured by synchronized methods. + */ +public class Tracker { + private HashMap torrents = new HashMap(); + + /* Adds peer to torrent's PeerSet. If no such Torrent present, + * creates the Torrent. + */ + public synchronized void announcePeer(InfoHash infoHash, Peer peer) { + Torrent torrent = torrents.get(infoHash); + if (torrent != null) { + torrent.putPeers(List.of(peer)); + } else { + // create torrent and initiate PeerSet with first announced peer + torrent = new Torrent(infoHash, peer); + torrents.put(torrent.getInfoHash(), torrent); + } + } + + /* Adds multiple peers to torrent. If no such Torrent present, + * creates the torrent. + */ + public synchronized > void announcePeers(InfoHash infoHash, T src) { + Torrent torrent = torrents.get(infoHash); + if (torrent != null) { + torrent.putPeers(src); + } else { + torrent = new Torrent(infoHash); + torrent.putPeers(src); + torrents.put(torrent.getInfoHash(), torrent); + } + } + + // Get list of peers at this very moment. + public synchronized > void getPeers(InfoHash infoHash, T dst) { + Torrent torrent = torrents.get(infoHash); + if (torrent != null) { + torrent.copyPeers(dst); + } + } +} diff --git a/src/test/java/com/mykola2312/retracker/tracker/TorrentTest.java b/src/test/java/com/mykola2312/retracker/tracker/TorrentTest.java index 5fccb82..5d42805 100644 --- a/src/test/java/com/mykola2312/retracker/tracker/TorrentTest.java +++ b/src/test/java/com/mykola2312/retracker/tracker/TorrentTest.java @@ -31,7 +31,7 @@ public class TorrentTest { List src = Arrays.asList(new Peer(newAddress("127.0.0.1", 1337)), new Peer(newAddress("127.0.0.1", 1338))); - torrent.updatePeers(src); + torrent.putPeers(src); { ArrayList copy = new ArrayList(); torrent.copyPeers(copy); diff --git a/src/test/java/com/mykola2312/retracker/tracker/TrackerTest.java b/src/test/java/com/mykola2312/retracker/tracker/TrackerTest.java new file mode 100644 index 0000000..3cd3220 --- /dev/null +++ b/src/test/java/com/mykola2312/retracker/tracker/TrackerTest.java @@ -0,0 +1,74 @@ +package com.mykola2312.retracker.tracker; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; + +public class TrackerTest { + private static InetSocketAddress newAddress(String addr, int port) throws UnknownHostException { + return new InetSocketAddress(InetAddress.getByName(addr), port); + } + + class TrackerJob implements Runnable { + private Tracker tracker; + private InfoHash infoHash; + private Peer peer; + + public TrackerJob(Tracker tracker, InfoHash infoHash, Peer peer) { + this.tracker = tracker; + this.infoHash = infoHash; + this.peer = peer; + } + + @Override + public void run() { + tracker.announcePeer(infoHash, peer); + } + } + + @Test + public void testTrackerPutAndGet() throws Exception { + final InfoHash infoHash = new InfoHash(); + infoHash.fromString("360775c6629eb06e60d90201aed1b7bc49a1ce16"); + + Tracker tracker = new Tracker(); + + ArrayList threads = new ArrayList(); + for (int i = 0; i < 32; i++) { + TrackerJob job = new TrackerJob(tracker, infoHash, + new Peer(newAddress("127.0.0.1", 1337 + i))); + Thread thread = new Thread(job); + thread.start(); + threads.add(thread); + + } + + // wait for all threads to finish + for (Thread thread : threads) { + thread.join(); + } + + ArrayList peers = new ArrayList(32); + tracker.getPeers(infoHash, peers); + + assertEquals(32, peers.size()); + + // check if every port present + for (int i = 0; i < 32; i++) { + int port = 1337 + i; + boolean peerPortPresent = false; + for (Peer peer : peers) { + if (peer.getAddress().getPort() == port) { + peerPortPresent = true; + } + } + + assertEquals(true, peerPortPresent); + } + } +}