Project 2022 · 2026Next — desktop GUI
java-p2p
Peer-to-peer file sharing over UDP, where a ring of super-nodes partitions the MD5 key space into a small DHT. Java 21, zero runtime dependencies, written from scratch.
- 66
- JUnit tests, in-process ring included
- 15
- v1 bugs found and fixed
- 0
- runtime dependencies
What it does
flowchart LR
SN1[super-node 1<br/>:9000] --> SN2[super-node 2<br/>:9001]
SN2 --> SN3[super-node 3<br/>:9002]
SN3 --> SN1
alice([peer alice]) --> SN1
bob([peer bob]) --> SN3
bob -. meta / chunk requests .-> alice
alice -. file chunks .-> bob
Peers join a ring of super-nodes, share any file in a folder, and download each other’s files chunk by chunk over UDP. The ring stores only who has what; file content always travels directly between peers.
Each resource is identified by the MD5 of its content. The ring splits the 2^128 key space into equal slices, and the last node owns the remainder, so no hash is ever left without an owner even when the node count doesn’t divide 2^128.
Protocol
Every datagram is a UTF-8 header line, optionally followed by a raw binary body, like a tiny HTTP. Control messages are text; file data never is.
| Message | Direction | Reply |
|---|---|---|
create <nickname> |
peer → super-node | OK or an error |
heartbeat <nickname> |
peer → super-node, every 5 s | none; peers expire after 15 s |
register <hash> <size> <name> |
peer → super-node | routed around the ring to the owner |
list |
peer → super-node | every resource in the ring |
meta <hash> |
peer → peer | size, chunk count and name |
chunk <hash> <i> |
peer → peer | up to 8 KiB of raw bytes |
Requests the current node doesn’t own travel the ring carrying their origin, and stop when they come back to it.
File transfer
Downloads are pull-based and stop-and-wait: one chunk at a time, retried after 500 ms, up to five
times. Chunks land in a .part file, and only after size and MD5 both match is it renamed into
place. A request carries a hash, never a path, so a peer can only serve files it chose to share.
The test suite includes an uploader that drops every third request. The download must still finish byte-identical.
Version 2
The first version was a 2022 assignment for Distributed Systems. Reviewing it years later turned up fifteen bugs, among them:
- the partition left the tail of the MD5 space without an owner for three nodes;
- ring messages never noticed they had returned to their origin, looping forever on unowned hashes;
- the next node was hard-coded as
localhost, so it only ever worked on one machine; - heartbeat expiry ran inside a
catchblock, so under traffic peers never expired.
v2 keeps the design, fixes every one of them, moves to Maven, Java 21 and CI. The original is
preserved under the v1.0.0-original tag.
What’s next
A desktop client in the style of a torrent app: files in the ring, per-chunk progress, connected peers and seeding, so the protocol can be watched instead of read from logs.