projects > from_scratch > bittorrent.go
1
A BitTorrent client, from scratch, in Go[github]
1
2
A BitTorrent client built from scratch in Go with no torrent
3
libraries — it hand-rolls the bencode codec, parses .torrent
4
files down to the info-hash, and announces to HTTP trackers to
5
discover a peer swarm.
6
7
Speaks the raw peer wire protocol (68-byte handshake, then
8
length-prefixed messages), downloads pieces in 16 KiB blocks with
9
SHA-1 verification, and saturates the swarm with a
10
goroutine-per-peer work queue. Magnet links are supported too,
11
fetching the torrent metadata itself from peers over ut_metadata
12
(BEP 9/10). Built end-to-end as the CodeCrafters 'Build Your Own
13
BitTorrent' challenge.
14
15
[Go][GitHub]
16
18
19
func (p Peer) Handshake(infoHash []byte, extensionSupported bool) (net.Conn, []byte, error) {
20
conn, err := net.DialTimeout("tcp", p.address, DialTimeout)
21
if err != nil {
22
return nil, nil, fmt.Errorf("can't connect to peer %s: %w", p.address, err)
23
}
24
25
handshakeMessage := make([]byte, 68)
26
// length of the protocol string (BitTorrent protocol) which is 19 (1 byte)
27
handshakeMessage[0] = 19
28
// the string BitTorrent protocol (19 bytes)
29
copy(handshakeMessage[1:20], []byte("BitTorrent protocol"))
30
// eight reserved bytes, which are all set to zero (8 bytes)
31
// or 20th bit flagged
32
if extensionSupported {
33
copy(handshakeMessage[20:28], []byte{0, 0, 0, 0, 0, 0b00010000, 0, 0})
34
}
35
// sha1 infohash (20 bytes) (NOT the hexadecimal representation, which is 40 bytes long)
36
copy(handshakeMessage[28:48], infoHash)
37
// peer id (20 bytes) (generate 20 random byte values)
38
copy(handshakeMessage[48:68], PeerId[:])
39
40
NORMAL~/oleksandr/projects/from_scratch/bittorrent.go.../from_scratch/bittorrent.gomainutf-8
Enter to open · :q to quit
keys
j / kmove the cursor line, or the tree cursor
hfocus the tree; in the tree, fold or go to the parent
l / Enterin the tree, unfold or open the file
gg / Gfirst / last line
clickput the cursor on a line; on README.sh, run the session again
:qquit to the shell
:help ?this table
q / Escclose this