mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-06 08:30:29 +00:00
refactoring package
This commit is contained in:
1 parent
b7347459eb
commit
137ed5d229
20 files changed
+528
-166
No files matched your search
@@ -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()
|
||||
}
|
||||
@@ -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)() {
|
||||
|
||||
}
|
||||
*/
|
||||
+6
-4
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bt
|
||||
package metainfo
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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<<uint(7-i%8)) > 0
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bt
|
||||
package peer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package torrent
|
||||
|
||||
type pieceState struct {
|
||||
complete bool
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user