From 137ed5d229d6126d07efbf881627f9201d7bd9bd Mon Sep 17 00:00:00 2001 From: liwei Date: Fri, 28 Jun 2019 17:45:53 +0800 Subject: [PATCH] refactoring package --- cmd/bt.go | 4 +- common/buffer.go | 41 ------ down/bt/client.go | 10 +- down/bt/client_test.go | 11 +- down/bt/{ => metainfo}/metainfo.go | 2 +- down/bt/peer/handshake.go | 51 ++++++++ down/bt/peer/message.go | 77 ++++++++++++ down/bt/peer/message_test.go | 79 ++++++++++++ down/bt/peer/peer.go | 30 +++++ down/bt/{peer.go => peer/peer.go.bak} | 5 +- .../{peer_test.go => peer/peer_test.go.bak} | 2 +- down/bt/torrent.go | 60 --------- down/bt/torrent/peer_state.go | 119 ++++++++++++++++++ down/bt/torrent/peer_state_test.go | 63 ++++++++++ down/bt/torrent/piece_state.go | 5 + down/bt/torrent/torrent.go | 27 ++++ down/bt/{ => tracker}/errors.go | 6 +- down/bt/{ => tracker}/tracker.go | 52 ++++---- down/bt/tracker/tracker_test.go | 26 ++++ down/bt/tracker_test.go | 24 ---- 20 files changed, 528 insertions(+), 166 deletions(-) delete mode 100644 common/buffer.go rename down/bt/{ => metainfo}/metainfo.go (99%) create mode 100644 down/bt/peer/handshake.go create mode 100644 down/bt/peer/message.go create mode 100644 down/bt/peer/message_test.go create mode 100644 down/bt/peer/peer.go rename down/bt/{peer.go => peer/peer.go.bak} (98%) rename down/bt/{peer_test.go => peer/peer_test.go.bak} (98%) delete mode 100644 down/bt/torrent.go create mode 100644 down/bt/torrent/peer_state.go create mode 100644 down/bt/torrent/peer_state_test.go create mode 100644 down/bt/torrent/piece_state.go create mode 100644 down/bt/torrent/torrent.go rename down/bt/{ => tracker}/errors.go (87%) rename down/bt/{ => tracker}/tracker.go (88%) create mode 100644 down/bt/tracker/tracker_test.go delete mode 100644 down/bt/tracker_test.go diff --git a/cmd/bt.go b/cmd/bt.go index 6d964247..18582621 100644 --- a/cmd/bt.go +++ b/cmd/bt.go @@ -1,10 +1,10 @@ package main import ( - "gopeed/down/bt" + "gopeed/down/bt/metainfo" ) func main() { - metaInfo, _ := bt.ParseFromFile("e:/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") + metaInfo, _ := metainfo.ParseFromFile("e:/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") metaInfo.Tracker() } diff --git a/common/buffer.go b/common/buffer.go deleted file mode 100644 index ce8514aa..00000000 --- a/common/buffer.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "bytes" - "encoding/binary" - "fmt" -) - -type User struct { - Age uint32 - Sex byte - Name [20]byte -} - -type Buffer struct { - buf []byte - limit int -} - -func NewBuffer(buf []byte) *Buffer { - return &Buffer{ - buf: buf, - limit: 0, - } -} - -func main() { - user := &User{1, 2, [20]byte{}} - buf := new(bytes.Buffer) - binary.Write(buf, binary.LittleEndian, user) - fmt.Println(buf.Bytes()) -} - -/*func (buffer Buffer) ReadByte() (byte, error) { - -} - -func (buffer Buffer) ReadByte() (byte, error)() { - -} -*/ diff --git a/down/bt/client.go b/down/bt/client.go index 06e7847e..0f589fba 100644 --- a/down/bt/client.go +++ b/down/bt/client.go @@ -2,13 +2,15 @@ package bt import ( "crypto/rand" + "gopeed/down/bt/metainfo" + "gopeed/down/bt/torrent" "net/url" ) type Client struct { PeerID [20]byte - torrents []Torrent + // torrents []torrent.Torrent } func NewClient() *Client { @@ -31,19 +33,19 @@ func (client *Client) AddTorrent(rawurl string) error { if err != nil { return err } - var metaInfo *MetaInfo + var metaInfo *metainfo.MetaInfo switch parse.Scheme { case "magnet": // TODO 磁力链接解析 default: - metaInfo, err = ParseFromFile(rawurl) + metaInfo, err = metainfo.ParseFromFile(rawurl) break } if err != nil { return err } - torrent := Torrent{ + torrent := torrent.Torrent{ client: client, MetaInfo: metaInfo, } diff --git a/down/bt/client_test.go b/down/bt/client_test.go index 3e709726..1a4c0747 100644 --- a/down/bt/client_test.go +++ b/down/bt/client_test.go @@ -5,20 +5,17 @@ import ( ) func TestClient_AddTorrent(t *testing.T) { - client := NewClient() - metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") + /*client := NewClient() + metaInfo, err := metainfo.ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") if err != nil { panic(err) } - torrent := &Torrent{ - client: client, - MetaInfo: metaInfo, - } + torrent := & torrent.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" torrent.MetaInfo.AnnounceList = [][]string{} err = torrent.Tracker() if err != nil { panic(err) return - } + }*/ } diff --git a/down/bt/metainfo.go b/down/bt/metainfo/metainfo.go similarity index 99% rename from down/bt/metainfo.go rename to down/bt/metainfo/metainfo.go index 2ff5af0f..af16b422 100644 --- a/down/bt/metainfo.go +++ b/down/bt/metainfo/metainfo.go @@ -1,4 +1,4 @@ -package bt +package metainfo import ( "crypto/sha1" diff --git a/down/bt/peer/handshake.go b/down/bt/peer/handshake.go new file mode 100644 index 00000000..5e30913b --- /dev/null +++ b/down/bt/peer/handshake.go @@ -0,0 +1,51 @@ +package peer + +import ( + "bytes" + "encoding/binary" +) + +const ( + ProtocolIdentifier = "BitTorrent protocol" + ProtocolIdentifierLength = 0x13 +) + +// Handshake of Peer wire protocol +// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake +type Handshake struct { + Pstrlen byte + Pstr [19]byte + Reserved [8]byte + InfoHash [20]byte + PeerID [20]byte +} + +func NewHandshake(reserved [8]byte, infoHash [20]byte, peerID [20]byte) *Handshake { + var arr [ProtocolIdentifierLength]byte + copy(arr[:], ProtocolIdentifier) + return &Handshake{ + Pstrlen: ProtocolIdentifierLength, + Pstr: arr, + Reserved: reserved, + InfoHash: infoHash, + PeerID: peerID, + } +} + +func (handshake *Handshake) Encode() []byte { + writer := new(bytes.Buffer) + err := binary.Write(writer, binary.BigEndian, handshake) + if err != nil { + panic(err) + } + return writer.Bytes() +} + +func (handshake *Handshake) Decode(buf []byte) error { + reader := bytes.NewReader(buf) + err := binary.Read(reader, binary.BigEndian, handshake) + if err != nil { + return err + } + return nil +} diff --git a/down/bt/peer/message.go b/down/bt/peer/message.go new file mode 100644 index 00000000..0e710297 --- /dev/null +++ b/down/bt/peer/message.go @@ -0,0 +1,77 @@ +package peer + +import ( + "encoding/binary" +) + +type MessageType int + +const ( + Keepalive MessageType = -1 + Choke MessageType = iota + Unchoke + Interested + NotInterested + Have + Bitfield + Request + Piece + Cancel +) + +// Message protocol +// length prefix| message ID | payload +// 4-byte | 1-byte | remaining bytes +// 100 | 5 | 99-bytes +// https://wiki.theory.org/index.php/BitTorrentSpecification#Messages +type Message struct { + Length uint32 + Type int + Payload []byte +} + +func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error) { + if !atEOF && len(data) > 4 { + length := int(binary.BigEndian.Uint32(data)) + if len(data)-4 >= length { + return length + 4, data[:length+4], nil + } + } + return +} + +func (msg *Message) Encode() []byte { + if msg.Type == -1 { + return make([]byte, 4) + } else { + buf := make([]byte, 5+len(msg.Payload)) + binary.BigEndian.PutUint32(buf, msg.Length) + buf[4] = byte(msg.Type) + buf = append(buf, msg.Payload[:]...) + return buf + } +} + +func (msg *Message) Decode(buf []byte) { + msg.Length = binary.BigEndian.Uint32(buf) + if msg.Length == 0 { + msg.Type = -1 + } else { + msg.Type = int(buf[4]) + if msg.Length > 4 { + msg.Payload = buf[5:] + } + } +} + +type MsgBitfield []byte + +// 某个分片是否下载完成 +func (mb MsgBitfield) get(i int) bool { + bts := []byte(mb) + index := i / 8 + if index >= len(bts) { + return false + } + return bts[index]&(1< 0 +} diff --git a/down/bt/peer/message_test.go b/down/bt/peer/message_test.go new file mode 100644 index 00000000..019e51fa --- /dev/null +++ b/down/bt/peer/message_test.go @@ -0,0 +1,79 @@ +package peer + +import ( + "strconv" + "testing" +) + +func TestMsgBitfield_get(t *testing.T) { + type args struct { + i int + } + tests := []struct { + name string + mb MsgBitfield + args args + want bool + }{ + { + "case-0", + MsgBitfield([]byte{convertByte("10000000")}), + args{ + 0, + }, + true, + }, + { + "case-1", + MsgBitfield([]byte{convertByte("01000000")}), + args{ + 1, + }, + true, + }, + { + "case-2", + MsgBitfield([]byte{convertByte("00000000")}), + args{ + 2, + }, + false, + }, + { + "case-10", + MsgBitfield([]byte{convertByte("00000000"), convertByte("00100000")}), + args{ + 10, + }, + true, + }, + { + "case-11", + MsgBitfield([]byte{convertByte("00000000"), convertByte("00100000")}), + args{ + 11, + }, + false, + }, + { + "case-20", + MsgBitfield([]byte{convertByte("00000000"), convertByte("00100000")}), + args{ + 20, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.mb.get(tt.args.i); got != tt.want { + t.Errorf("MsgBitfield.get() = %v, want %v", got, tt.want) + } + }) + } +} + +func convertByte(bit string) byte { + i, _ := strconv.ParseUint(bit, 2, 8) + return byte(i) +} diff --git a/down/bt/peer/peer.go b/down/bt/peer/peer.go new file mode 100644 index 00000000..f36465f0 --- /dev/null +++ b/down/bt/peer/peer.go @@ -0,0 +1,30 @@ +package peer + +import ( + "encoding/binary" + "math/rand" + "net" + "strconv" +) + +type Peer struct { + IP uint32 + Port uint16 +} + +func (peer *Peer) Address() string { + bts := make([]byte, 4) + binary.BigEndian.PutUint32(bts, peer.IP) + return net.IP(bts).String() + ":" + strconv.Itoa(int(peer.Port)) +} + +// 生成Peer ID,规则为前三位固定字母(-GP)+SemVer(xyz)+End(-),后面随机生成 +// 参考:https://wiki.theory.org/index.php/BitTorrentSpecification#peer_id +func GenPeerID() [20]byte { + peerID := [20]byte{'-', 'G', 'P', '0', '0', '1', '-'} + _, err := rand.Read(peerID[7:]) + if err != nil { + panic(err) + } + return peerID +} diff --git a/down/bt/peer.go b/down/bt/peer/peer.go.bak similarity index 98% rename from down/bt/peer.go rename to down/bt/peer/peer.go.bak index 776d4ab2..3142c056 100644 --- a/down/bt/peer.go +++ b/down/bt/peer/peer.go.bak @@ -1,4 +1,4 @@ -package bt +package peer import ( "bufio" @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "github.com/cenkalti/mse" + "gopeed/down/bt/metainfo" "io" "net" "strconv" @@ -310,6 +311,6 @@ func decodeMessage(buf []byte) *Message { return msg } -func doBitfield(peer *Peer, metaInfo *MetaInfo, conn net.Conn) { +func doBitfield(peer *Peer, metaInfo *metainfo.MetaInfo, conn net.Conn) { } diff --git a/down/bt/peer_test.go b/down/bt/peer/peer_test.go.bak similarity index 98% rename from down/bt/peer_test.go rename to down/bt/peer/peer_test.go.bak index 69abd297..2b797c7a 100644 --- a/down/bt/peer_test.go +++ b/down/bt/peer/peer_test.go.bak @@ -1,4 +1,4 @@ -package bt +package peer import ( "fmt" diff --git a/down/bt/torrent.go b/down/bt/torrent.go deleted file mode 100644 index 136350e3..00000000 --- a/down/bt/torrent.go +++ /dev/null @@ -1,60 +0,0 @@ -package bt - -import ( - "fmt" - "io" -) - -type Torrent struct { - client *Client - - MetaInfo *MetaInfo - Peers []Peer -} - -// Handshake of Peer wire protocol -// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake -func (torrent *Torrent) doHandshake(peer *Peer) error { - conn, err := dialMse(peer.Address(), peer.Torrent.MetaInfo.InfoHash) - if err != nil { - return err - } - defer conn.Close() - - metaInfo := peer.Torrent.MetaInfo - peerID := peer.Torrent.client.PeerID - - reserved := [8]byte{} - /* reserved[5] = 0x10 - reserved[6] = 0x0 - reserved[7] = 0x5*/ - handshakeReq := newHandshake(reserved, metaInfo.InfoHash, peerID) - buf, err := handshakeReq.encode() - if err != nil { - return err - } - conn.Write(buf) - - var read [68]byte - _, err = io.ReadFull(conn, read[:]) - if err != nil { - return err - } - handshakeRes := new(Handshake) - err = handshakeRes.decode(read[:]) - if err != nil { - return err - } - if handshakeRes.InfoHash != handshakeReq.InfoHash { - return err - } - return nil -} - -func (torrent *Torrent) Download() { - err := torrent.Tracker() - if err != nil { - fmt.Println(err) - return - } -} diff --git a/down/bt/torrent/peer_state.go b/down/bt/torrent/peer_state.go new file mode 100644 index 00000000..565dc864 --- /dev/null +++ b/down/bt/torrent/peer_state.go @@ -0,0 +1,119 @@ +package torrent + +import ( + "bufio" + "fmt" + "github.com/cenkalti/mse" + "gopeed/down/bt/peer" + "io" + "net" +) + +type peerState struct { + torrent *Torrent + *peer.Peer + conn net.Conn + // this client is choking the peer + amChoking bool + // this client is interested in the peer + amInterested bool + // peer is choking this client + peerChoking bool + // peer is interested in this client + peerInterested bool +} + +// 使用MSE加密来避免运营商对bt流量的封锁,基本上现在市面上BT客户端都默认开启了,不用MSE的话很多Peer拒绝连接 +// http://wiki.vuze.com/w/Message_Stream_Encryption +func (ps *peerState) dialMse() error { + conn, err := net.Dial("tcp", ps.Address()) + if err != nil { + return err + } + mseConn := mse.WrapConn(conn) + _, err = mseConn.HandshakeOutgoing(ps.torrent.MetaInfo.InfoHash[:], mse.PlainText, nil) + if err != nil { + mseConn.Close() + return err + } + ps.conn = mseConn + return nil +} + +// Handshake of Peer wire protocol +// https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake +func (ps *peerState) handshake() (*peer.Handshake, error) { + handshakeRes, err := func() (*peer.Handshake, error) { + handshakeReq := peer.NewHandshake([8]byte{}, ps.torrent.MetaInfo.InfoHash, ps.torrent.PeerID) + _, err := ps.conn.Write(handshakeReq.Encode()) + if err != nil { + return nil, err + } + var read [68]byte + _, err = io.ReadFull(ps.conn, read[:]) + if err != nil { + return nil, err + } + handshakeRes := &peer.Handshake{} + err = handshakeRes.Decode(read[:]) + if err != nil { + return nil, err + } + // InfoHash不匹配 + if handshakeRes.InfoHash != handshakeReq.InfoHash { + return nil, fmt.Errorf("info_hash not currently serving") + } + return handshakeRes, nil + }() + if err != nil { + ps.conn.Close() + return nil, err + } + // init state + ps.amChoking = true + ps.amInterested = false + ps.peerChoking = true + ps.peerInterested = false + return handshakeRes, nil +} + +// +func (ps *peerState) download() error { + scanner := bufio.NewScanner(ps.conn) + scanner.Split(peer.SplitMessage) + for scanner.Scan() { + buf := scanner.Bytes() + message := &peer.Message{} + message.Decode(buf) + switch peer.MessageType(message.Type) { + case peer.Keepalive: + break + case peer.Choke: + ps.peerChoking = true + break + case peer.Unchoke: + ps.peerChoking = false + break + case peer.Interested: + ps.peerInterested = true + break + case peer.NotInterested: + ps.peerInterested = false + break + case peer.Have: + break + case peer.Bitfield: + bitfield := peer.MsgBitfield(message.Payload) + // 判断peer能提供需要下载的文件分片 + + break + case peer.Request: + break + case peer.Piece: + break + case peer.Cancel: + break + } + } + return nil +} diff --git a/down/bt/torrent/peer_state_test.go b/down/bt/torrent/peer_state_test.go new file mode 100644 index 00000000..dc60bd20 --- /dev/null +++ b/down/bt/torrent/peer_state_test.go @@ -0,0 +1,63 @@ +package torrent + +import ( + "fmt" + "gopeed/down/bt/metainfo" + "gopeed/down/bt/peer" + "gopeed/down/bt/tracker" + "testing" +) + +func Test_peerState_handshake(t *testing.T) { + torrent := buildTorrent() + tracker := &tracker.Tracker{ + PeerID: torrent.PeerID, + MetaInfo: torrent.MetaInfo, + } + peers, err := tracker.Tracker() + if err != nil { + panic(err) + } + fmt.Println("Tracker end") + ps := getUsablePeer(torrent, peers) + + // 发出握手请求 + handshake, err := ps.handshake() + fmt.Println("Handshake end") + if err != nil { + fmt.Println(ps.Address()) + panic(err) + } + fmt.Println(handshake) +} + +func buildTorrent() *Torrent { + metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") + if err != nil { + panic(err) + } + metaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" + metaInfo.AnnounceList = [][]string{} + return NewTorrent(peer.GenPeerID(), metaInfo) +} + +func getUsablePeer(torrent *Torrent, peers []peer.Peer) *peerState { + ch := make(chan *peerState, 1) + for i := range peers { + go peerTest(torrent, &peers[i], ch) + } + return <-ch +} + +func peerTest(torrent *Torrent, peer *peer.Peer, ch chan *peerState) { + ps := &peerState{ + torrent: torrent, + Peer: peer, + } + // 连接至peer + err := ps.dialMse() + if err != nil { + return + } + ch <- ps +} diff --git a/down/bt/torrent/piece_state.go b/down/bt/torrent/piece_state.go new file mode 100644 index 00000000..8967138e --- /dev/null +++ b/down/bt/torrent/piece_state.go @@ -0,0 +1,5 @@ +package torrent + +type pieceState struct { + complete bool +} diff --git a/down/bt/torrent/torrent.go b/down/bt/torrent/torrent.go new file mode 100644 index 00000000..d2bfa19a --- /dev/null +++ b/down/bt/torrent/torrent.go @@ -0,0 +1,27 @@ +package torrent + +import ( + "gopeed/down/bt/metainfo" + "gopeed/down/bt/peer" +) + +type Torrent struct { + PeerID [20]byte + MetaInfo *metainfo.MetaInfo + PieceState []pieceState + + Peers []peerState +} + +func NewTorrent(peerID [20]byte, metaInfo *metainfo.MetaInfo) *Torrent { + torrent := &Torrent{ + PeerID: peerID, + MetaInfo: metaInfo, + PieceState: make([]pieceState, len(metaInfo.Info.Pieces)), + } + return torrent +} + +// 检查一个peer能否提供需要下载的文件分片 +func CheckPeer(bitfield peer.MsgBitfield) { +} diff --git a/down/bt/errors.go b/down/bt/tracker/errors.go similarity index 87% rename from down/bt/errors.go rename to down/bt/tracker/errors.go index daf1a72c..8d77aa4d 100644 --- a/down/bt/errors.go +++ b/down/bt/tracker/errors.go @@ -1,4 +1,4 @@ -package bt +package tracker import ( "fmt" @@ -11,6 +11,10 @@ const ( ErrResponse ErrorCode = -2 ) +type BtError interface { + error +} + type TrackerError struct { Code ErrorCode Err error diff --git a/down/bt/tracker.go b/down/bt/tracker/tracker.go similarity index 88% rename from down/bt/tracker.go rename to down/bt/tracker/tracker.go index 986c1e38..2a8ef562 100644 --- a/down/bt/tracker.go +++ b/down/bt/tracker/tracker.go @@ -1,9 +1,11 @@ -package bt +package tracker import ( "encoding/binary" "errors" "fmt" + "gopeed/down/bt/metainfo" + "gopeed/down/bt/peer" "io" "io/ioutil" "math" @@ -154,7 +156,7 @@ type udpAnnounceResponse struct { interval uint32 leechers uint32 seeders uint32 - peers []Peer + peers []peer.Peer } func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse { @@ -166,11 +168,11 @@ func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse { seeders: binary.BigEndian.Uint32(buf[16:20]), } count := (len(buf) - 20) / 6 - response.peers = make([]Peer, count, count) + response.peers = make([]peer.Peer, count, count) for i := 0; i < count; i++ { ipBegin := 20 + 6*i portBegin := ipBegin + 4 - response.peers[i] = Peer{ + response.peers[i] = peer.Peer{ IP: binary.BigEndian.Uint32(buf[ipBegin:portBegin]), Port: binary.BigEndian.Uint16(buf[portBegin : portBegin+2]), } @@ -178,7 +180,12 @@ func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse { return response } -func (torrent *Torrent) connect(conn *net.UDPConn, timeout int64) (response *udpConnectResponse, err error) { +type Tracker struct { + PeerID [20]byte + MetaInfo *metainfo.MetaInfo +} + +func (tracker *Tracker) connect(conn *net.UDPConn, timeout int64) (response *udpConnectResponse, err error) { request := newUdpConnectRequest() buf := request.encode() conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout))) @@ -195,13 +202,13 @@ func (torrent *Torrent) connect(conn *net.UDPConn, timeout int64) (response *udp return } -func (torrent *Torrent) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) { +func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) { request := newUdpAnnounceRequest(connectionId) - request.infoHash = torrent.MetaInfo.InfoHash - request.peerID = torrent.client.PeerID + request.infoHash = tracker.MetaInfo.InfoHash + request.peerID = tracker.PeerID request.downloaded = 0 request.uploaded = 0 - request.left = torrent.MetaInfo.FileSize - request.downloaded + request.left = tracker.MetaInfo.FileSize - request.downloaded request.event = 0 request.ip = 0 request.key = 0 @@ -213,7 +220,6 @@ func (torrent *Torrent) announce(conn *net.UDPConn, timeout int64, connectionId buf := make([]byte, 512) len, err := conn.Read(buf) if err != nil { - fmt.Println(err) return } response = newUdpAnnounceResponse(buf[:len]) @@ -221,8 +227,8 @@ func (torrent *Torrent) announce(conn *net.UDPConn, timeout int64, connectionId } // 通过tracker服务器获取peer信息 -func (torrent *Torrent) Tracker() (err error) { - metaInfo := torrent.MetaInfo +func (tracker *Tracker) Tracker() (peers []peer.Peer, err error) { + metaInfo := tracker.MetaInfo checkAnnounceList := func(announceList [][]string) bool { if len(announceList) > 0 { @@ -240,7 +246,7 @@ func (torrent *Torrent) Tracker() (err error) { for _, announceArr := range metaInfo.AnnounceList { if len(announceArr) > 0 { for _, announce := range announceArr { - torrent.Peers, err = torrent.doTracker(announce) + peers, err = tracker.doTracker(announce) if err == nil { return } @@ -248,21 +254,21 @@ func (torrent *Torrent) Tracker() (err error) { } } } else { - torrent.Peers, err = torrent.doTracker(metaInfo.Announce) + peers, err = tracker.doTracker(metaInfo.Announce) } return } -func (torrent *Torrent) doTracker(announce string) (peers []Peer, err error) { +func (tracker *Tracker) doTracker(announce string) (peers []peer.Peer, err error) { if announce != "" { url, _ := url.Parse(announce) switch url.Scheme { case "http", "https": // TODO UDP test break - return torrent.httpTracker(url) + return tracker.httpTracker(url) case "udp", "udp4", "udp6": - return torrent.udpTracker(url) + return tracker.udpTracker(url) default: return nil, errors.New("unsupported protocol") } @@ -271,9 +277,9 @@ func (torrent *Torrent) doTracker(announce string) (peers []Peer, err error) { } // http://bittorrent.org/beps/bep_0003.html#trackers -func (torrent *Torrent) httpTracker(url *url.URL) (peers []Peer, err error) { - metaInfo := torrent.MetaInfo - peerID := torrent.client.PeerID +func (tracker *Tracker) httpTracker(url *url.URL) (peers []peer.Peer, err error) { + metaInfo := tracker.MetaInfo + peerID := tracker.PeerID query := url.Query() query.Add("info_hash", string(metaInfo.InfoHash[:])) @@ -308,7 +314,7 @@ func (torrent *Torrent) httpTracker(url *url.URL) (peers []Peer, err error) { } // http://bittorrent.org/beps/bep_0015.html -func (torrent *Torrent) udpTracker(url *url.URL) (peers []Peer, err error) { +func (tracker *Tracker) udpTracker(url *url.URL) (peers []peer.Peer, err error) { conn, err := dial(url) if err != nil { return @@ -325,7 +331,7 @@ func (torrent *Torrent) udpTracker(url *url.URL) (peers []Peer, err error) { timeout := int64(udpConnectTimeout * math.Pow(2, float64(n))) switch state { case 0: - connectResponse, err = torrent.connect(conn, timeout) + connectResponse, err = tracker.connect(conn, timeout) if err != nil { if oe, ok := err.(*net.OpError); ok && oe.Timeout() { break @@ -337,7 +343,7 @@ func (torrent *Torrent) udpTracker(url *url.URL) (peers []Peer, err error) { state = 1 break case 1: - announceResponse, err = torrent.announce(conn, timeout, connectResponse.connectionId) + announceResponse, err = tracker.announce(conn, timeout, connectResponse.connectionId) if err != nil { if oe, ok := err.(*net.OpError); ok && oe.Timeout() { break diff --git a/down/bt/tracker/tracker_test.go b/down/bt/tracker/tracker_test.go new file mode 100644 index 00000000..5b8b5b08 --- /dev/null +++ b/down/bt/tracker/tracker_test.go @@ -0,0 +1,26 @@ +package tracker + +import ( + "fmt" + "gopeed/down/bt/metainfo" + "testing" +) + +func TestTracker_Tracker(t *testing.T) { + metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") + if err != nil { + panic(err) + } + tracker := &Tracker{ + PeerID: [20]byte{}, + MetaInfo: metaInfo, + } + tracker.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" + tracker.MetaInfo.AnnounceList = [][]string{} + peers, err := tracker.Tracker() + if err != nil { + panic(err) + return + } + fmt.Println(len(peers)) +} diff --git a/down/bt/tracker_test.go b/down/bt/tracker_test.go deleted file mode 100644 index dde5cdd3..00000000 --- a/down/bt/tracker_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package bt - -import ( - "testing" -) - -func TestTracker_Tracker(t *testing.T) { - client := NewClient() - metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - if err != nil { - panic(err) - } - torrent := &Torrent{ - client: client, - MetaInfo: metaInfo, - } - torrent.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" - torrent.MetaInfo.AnnounceList = [][]string{} - err = torrent.Tracker() - if err != nil { - panic(err) - return - } -}