MSE support

This commit is contained in:
liwei committed 2019-05-31 18:29:04 +08:00
1 parent 03fbdcda60
commit dbc58a209d
11 files changed
+309 -343

No files matched your search

-1
View File
@@ -1 +0,0 @@
package bt
+55
View File
@@ -0,0 +1,55 @@
package bt
import (
"crypto/rand"
"net/url"
)
type Client struct {
PeerID [20]byte
torrents []Torrent
}
func NewClient() *Client {
client := &Client{}
// 生成Peer ID,规则为前三位固定字母(-GP)+SemVer(xyz)+End(-),后面随机生成
// 参考:https://wiki.theory.org/index.php/BitTorrentSpecification#peer_id
peerID := [20]byte{'-', 'G', 'P', '0', '0', '1', '-'}
_, err := rand.Read(peerID[7:])
if err != nil {
panic(err)
}
client.PeerID = peerID
return client
}
func (client *Client) AddTorrent(rawurl string) error {
parse, err := url.Parse(rawurl)
if err != nil {
return err
}
var metaInfo *MetaInfo
switch parse.Scheme {
case "magnet":
// TODO 磁力链接解析
default:
metaInfo, err = ParseFromFile(rawurl)
break
}
if err != nil {
return err
}
torrent := Torrent{
client: client,
MetaInfo: metaInfo,
}
client.torrents = append(client.torrents, torrent)
torrent.Download()
return nil
}
+10
View File
@@ -0,0 +1,10 @@
package bt
import (
"testing"
)
func TestClient_AddTorrent(t *testing.T) {
client := NewClient()
client.AddTorrent("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
}
+25 -25
View File
@@ -11,24 +11,18 @@ type MetaInfo struct {
// tracker URL
// http://bittorrent.org/beps/bep_0003.html#metainfo-files
Announce string `json:"announce"`
// tracker URL
// http://bittorrent.org/beps/bep_0012.html
AnnounceList [][]string `json:"announce-list"`
Comment string `json:"comment"`
CreatedBy string `json:"created by"`
CreationDate int64 `json:"creation date"`
Encoding string `json:"encoding"`
Comment string `json:"comment"`
CreatedBy string `json:"created by"`
CreationDate int64 `json:"creation date"`
Encoding string `json:"encoding"`
// http://bittorrent.org/beps/bep_0019.html
UrlList []string `json:"url-list"`
Info *Info `json:"info"`
Info *Info `json:"info"`
InfoHash [20]byte `json:"-"`
ExtraInfo `json:"-"`
}
type Info struct {
@@ -44,19 +38,9 @@ type File struct {
Path string `json:"path"`
}
func (metaInfo *MetaInfo) GetTotalSize() uint64 {
if metaInfo.Info != nil {
if len(metaInfo.Info.Files) > 0 {
var length uint64
for _, f := range metaInfo.Info.Files {
length += f.Length
}
return length
} else {
return metaInfo.Info.Length
}
}
return 0
type ExtraInfo struct {
InfoHash [20]byte
FileSize uint64
}
func ParseFromFile(path string) (*MetaInfo, error) {
@@ -91,5 +75,21 @@ func ParseFromFile(path string) (*MetaInfo, error) {
}
}
}
metaInfo.FileSize = getTotalSize(&metaInfo)
return &metaInfo, nil
}
func getTotalSize(metaInfo *MetaInfo) uint64 {
if metaInfo.Info != nil {
if len(metaInfo.Info.Files) > 0 {
var length uint64
for _, f := range metaInfo.Info.Files {
length += f.Length
}
return length
} else {
return metaInfo.Info.Length
}
}
return 0
}
+28 -8
View File
@@ -5,6 +5,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"github.com/cenkalti/mse"
"io"
"net"
"strconv"
@@ -30,6 +31,9 @@ const (
type Peer struct {
IP uint32
Port uint16
Torrent *Torrent
conn net.Conn
}
func (peer *Peer) Address() string {
@@ -82,16 +86,29 @@ func (handshake *Handshake) decode(buf []byte) error {
return nil
}
func (peer *Peer) DoDownload(metaInfo *MetaInfo, peerId [20]byte) error {
func dialMse(address string, infoHash [20]byte) (net.Conn, error) {
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
}
mseConn := mse.WrapConn(conn)
_, err = mseConn.HandshakeOutgoing(infoHash[:], mse.PlainText, nil)
if err != nil {
return nil, err
}
return mseConn, nil
}
func (peer *Peer) DoDownload() error {
if peer.Port == 0 {
return fmt.Errorf("error port %d", peer.Port)
}
conn, err := net.Dial("tcp", peer.Address())
conn, err := dialMse(peer.Address(), peer.Torrent.MetaInfo.InfoHash)
if err != nil {
return err
}
defer conn.Close()
err = doHandshake(peer, metaInfo, peerId, conn)
err = peer.doHandshake(conn)
if err != nil {
return err
}
@@ -204,12 +221,15 @@ func (peer *Peer) DoDownload(metaInfo *MetaInfo, peerId [20]byte) error {
// Handshake of Peer wire protocol
// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake
func doHandshake(peer *Peer, metaInfo *MetaInfo, peerId [20]byte, conn net.Conn) error {
func (peer *Peer) doHandshake(conn net.Conn) error {
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)
/* reserved[5] = 0x10
reserved[6] = 0x0
reserved[7] = 0x5*/
handshakeReq := NewHandshake(reserved, metaInfo.InfoHash, peerID)
buf, err := handshakeReq.encode()
if err != nil {
return err
-157
View File
@@ -1,157 +0,0 @@
package bt
import (
"encoding/binary"
"fmt"
"log"
"testing"
)
func TestDoDownload(t *testing.T) {
metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
if err != nil {
log.Fatal(err)
}
peers, err := metaInfo.Tracker()
if err != nil {
log.Fatal(err)
}
testPeer := peers[0]
fmt.Println(testPeer.Address())
peerId, err := GenPeerID()
if err != nil {
log.Fatal(err)
}
type fields struct {
IP uint32
Port uint16
}
type args struct {
metaInfo *MetaInfo
peerId [20]byte
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
"GOTS08E05",
fields{
testPeer.IP,
testPeer.Port,
},
args{
metaInfo,
peerId,
},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
peer := &Peer{
IP: tt.fields.IP,
Port: tt.fields.Port,
}
if err := peer.DoDownload(tt.args.metaInfo, tt.args.peerId); (err != nil) != tt.wantErr {
t.Errorf("Peer.DoDownload() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestFindPeer(t *testing.T) {
metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
if err != nil {
log.Fatal(err)
}
peers, err := metaInfo.Tracker()
if err != nil {
log.Fatal(err)
}
// peerId, err := GenPeerID()
peerId, err := [20]byte{0x2d, 0x55, 0x54, 0x33, 0x35, 0x35, 0x53, 0x2d, 0xaf, 0xb0, 0xec, 0xdc, 0xbf, 0xd1, 0x02, 0x93, 0xd6, 0xf4, 0x9b, 0x24}, nil
if err != nil {
log.Fatal(err)
}
ch := make(chan Peer)
for i, peer := range peers {
go func(i int, peer Peer) {
fmt.Println(peer.Address())
err := peer.DoDownload(metaInfo, peerId)
if err == nil {
ch <- peer
} else {
fmt.Printf("%d:%v\n", i, err)
}
}(i, peer)
}
peer := <-ch
fmt.Println(peer.Address())
}
func TestDoDownloadSpecialPeer(t *testing.T) {
metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
if err != nil {
log.Fatal(err)
}
// 101.188.129.156:14475
testPeer := Peer{
binary.BigEndian.Uint32([]byte{101, 188, 129, 156}),
14475,
}
fmt.Println(testPeer.Address())
peerId, err := GenPeerID()
if err != nil {
log.Fatal(err)
}
type fields struct {
IP uint32
Port uint16
}
type args struct {
metaInfo *MetaInfo
peerId [20]byte
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
"GOTS08E05",
fields{
testPeer.IP,
testPeer.Port,
},
args{
metaInfo,
peerId,
},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
peer := &Peer{
IP: tt.fields.IP,
Port: tt.fields.Port,
}
if err := peer.DoDownload(tt.args.metaInfo, tt.args.peerId); (err != nil) != tt.wantErr {
t.Errorf("Peer.DoDownload() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
+24
View File
@@ -0,0 +1,24 @@
package bt
import (
"fmt"
)
type Torrent struct {
client *Client
MetaInfo *MetaInfo
Peers []Peer
}
func (torrent *Torrent) Download() {
t := NewTracker(torrent)
peers, err := t.Tracker()
if err != nil {
fmt.Println(err)
return
}
peers[0].Torrent = torrent
peers[0].DoDownload()
}
+153 -151
View File
@@ -1,7 +1,6 @@
package bt
import (
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
@@ -26,6 +25,14 @@ const (
udpActionError = 3
)
type Tracker struct {
torrent *Torrent
}
func NewTracker(torrent *Torrent) *Tracker {
return &Tracker{torrent: torrent}
}
/*
Offset Size Name Value
0 64-bit integer protocol_id 0x41727101980 // magic constant
@@ -34,25 +41,25 @@ const (
16
Per https://www.libtorrent.org/udp_tracker_protocol.html#connecting
*/
type UdpConnectRequest struct {
ProtocolId uint64
Action uint32
TransactionId uint32
type udpConnectRequest struct {
protocolId uint64
action uint32
transactionId uint32
}
func NewUdpConnectRequest() *UdpConnectRequest {
return &UdpConnectRequest{
ProtocolId: udpConnectRequestMagic,
Action: udpActionConnect,
TransactionId: mrand.Uint32(),
func newUdpConnectRequest() *udpConnectRequest {
return &udpConnectRequest{
protocolId: udpConnectRequestMagic,
action: udpActionConnect,
transactionId: mrand.Uint32(),
}
}
func (req *UdpConnectRequest) Encode() []byte {
func (req *udpConnectRequest) Encode() []byte {
buf := make([]byte, 16)
binary.BigEndian.PutUint64(buf[:8], req.ProtocolId)
binary.BigEndian.PutUint32(buf[8:12], req.Action)
binary.BigEndian.PutUint32(buf[12:], req.TransactionId)
binary.BigEndian.PutUint64(buf[:8], req.protocolId)
binary.BigEndian.PutUint32(buf[8:12], req.action)
binary.BigEndian.PutUint32(buf[12:], req.transactionId)
return buf
}
@@ -63,17 +70,17 @@ func (req *UdpConnectRequest) Encode() []byte {
8 64-bit integer connection_id
16
*/
type UdpConnectResponse struct {
Action uint32
TransactionId uint32
ConnectionId uint64
type udpConnectResponse struct {
action uint32
transactionId uint32
connectionId uint64
}
func NewUdpConnectResponse(buf []byte) *UdpConnectResponse {
return &UdpConnectResponse{
Action: binary.BigEndian.Uint32(buf[:4]),
TransactionId: binary.BigEndian.Uint32(buf[4:8]),
ConnectionId: binary.BigEndian.Uint64(buf[8:]),
func newUdpConnectResponse(buf []byte) *udpConnectResponse {
return &udpConnectResponse{
action: binary.BigEndian.Uint32(buf[:4]),
transactionId: binary.BigEndian.Uint32(buf[4:8]),
connectionId: binary.BigEndian.Uint64(buf[8:]),
}
}
@@ -88,52 +95,52 @@ func NewUdpConnectResponse(buf []byte) *UdpConnectResponse {
64 64-bit integer left
72 64-bit integer uploaded
80 32-bit integer event 0 // 0: none; 1: completed; 2: started; 3: stopped
84 32-bit integer IP address 0 // default
84 32-bit integer ip address 0 // default
88 32-bit integer key
92 32-bit integer num_want -1 // default
96 16-bit integer port
98
Per https://www.libtorrent.org/udp_tracker_protocol.html#announcing
*/
type UdpAnnounceRequest struct {
ConnectionId uint64
Action uint32
TransactionId uint32
InfoHash [20]byte
PeerID [20]byte
Downloaded uint64
Left uint64
Uploaded uint64
Event uint32
IP uint32
Key uint32
NumWant int32
Port uint16
type udpAnnounceRequest struct {
connectionId uint64
action uint32
transactionId uint32
infoHash [20]byte
peerID [20]byte
downloaded uint64
left uint64
uploaded uint64
event uint32
ip uint32
key uint32
numWant int32
port uint16
}
func NewUdpAnnounceRequest(connectionId uint64) *UdpAnnounceRequest {
return &UdpAnnounceRequest{
ConnectionId: connectionId,
Action: udpActionAnnounce,
TransactionId: mrand.Uint32(),
func newUdpAnnounceRequest(connectionId uint64) *udpAnnounceRequest {
return &udpAnnounceRequest{
connectionId: connectionId,
action: udpActionAnnounce,
transactionId: mrand.Uint32(),
}
}
func (req *UdpAnnounceRequest) encode() []byte {
func (req *udpAnnounceRequest) encode() []byte {
buf := make([]byte, 98, 98)
binary.BigEndian.PutUint64(buf[:8], req.ConnectionId)
binary.BigEndian.PutUint32(buf[8:12], req.Action)
binary.BigEndian.PutUint32(buf[12:16], req.TransactionId)
copy(buf[16:36], req.InfoHash[:])
copy(buf[36:56], req.PeerID[:])
binary.BigEndian.PutUint64(buf[56:64], req.Downloaded)
binary.BigEndian.PutUint64(buf[64:72], req.Left)
binary.BigEndian.PutUint64(buf[72:80], req.Uploaded)
binary.BigEndian.PutUint32(buf[80:84], req.Event)
binary.BigEndian.PutUint32(buf[84:88], req.IP)
binary.BigEndian.PutUint32(buf[88:92], req.Key)
binary.BigEndian.PutUint32(buf[92:96], uint32(req.NumWant))
binary.BigEndian.PutUint16(buf[96:98], req.Port)
binary.BigEndian.PutUint64(buf[:8], req.connectionId)
binary.BigEndian.PutUint32(buf[8:12], req.action)
binary.BigEndian.PutUint32(buf[12:16], req.transactionId)
copy(buf[16:36], req.infoHash[:])
copy(buf[36:56], req.peerID[:])
binary.BigEndian.PutUint64(buf[56:64], req.downloaded)
binary.BigEndian.PutUint64(buf[64:72], req.left)
binary.BigEndian.PutUint64(buf[72:80], req.uploaded)
binary.BigEndian.PutUint32(buf[80:84], req.event)
binary.BigEndian.PutUint32(buf[84:88], req.ip)
binary.BigEndian.PutUint32(buf[88:92], req.key)
binary.BigEndian.PutUint32(buf[92:96], uint32(req.numWant))
binary.BigEndian.PutUint16(buf[96:98], req.port)
return buf
}
@@ -144,34 +151,34 @@ func (req *UdpAnnounceRequest) encode() []byte {
8 32-bit integer interval
12 32-bit integer leechers
16 32-bit integer seeders
20 + 6 * n 32-bit integer IP address
20 + 6 * n 32-bit integer ip address
24 + 6 * n 16-bit integer TCP port
20 + 6 * N
Per https://www.libtorrent.org/udp_tracker_protocol.html#announcing
*/
type UdpAnnounceResponse struct {
Action uint32
TransactionId uint32
Interval uint32
Leechers uint32
Seeders uint32
Peers []Peer
type udpAnnounceResponse struct {
action uint32
transactionId uint32
interval uint32
leechers uint32
seeders uint32
peers []Peer
}
func NewUdpAnnounceResponse(buf []byte) *UdpAnnounceResponse {
response := &UdpAnnounceResponse{
Action: binary.BigEndian.Uint32(buf[:4]),
TransactionId: binary.BigEndian.Uint32(buf[4:8]),
Interval: binary.BigEndian.Uint32(buf[8:12]),
Leechers: binary.BigEndian.Uint32(buf[12:16]),
Seeders: binary.BigEndian.Uint32(buf[16:20]),
func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse {
response := &udpAnnounceResponse{
action: binary.BigEndian.Uint32(buf[:4]),
transactionId: binary.BigEndian.Uint32(buf[4:8]),
interval: binary.BigEndian.Uint32(buf[8:12]),
leechers: binary.BigEndian.Uint32(buf[12:16]),
seeders: binary.BigEndian.Uint32(buf[16:20]),
}
count := (len(buf) - 20) / 6
response.Peers = make([]Peer, count, count)
response.peers = make([]Peer, count, count)
for i := 0; i < count; i++ {
ipBegin := 20 + 6*i
portBegin := ipBegin + 4
response.Peers[i] = Peer{
response.peers[i] = Peer{
IP: binary.BigEndian.Uint32(buf[ipBegin:portBegin]),
Port: binary.BigEndian.Uint16(buf[portBegin : portBegin+2]),
}
@@ -179,33 +186,8 @@ func NewUdpAnnounceResponse(buf []byte) *UdpAnnounceResponse {
return response
}
func dial(trackerUrl *url.URL) (*net.UDPConn, error) {
host, portStr, err := net.SplitHostPort(trackerUrl.Host)
if err != nil {
fmt.Println(err)
return nil, err
}
port, err := strconv.ParseInt(portStr, 10, 32)
if err != nil {
fmt.Println(err)
return nil, err
}
ip, err := lookupIP(host, trackerUrl.Scheme)
if err != nil {
fmt.Println(err)
return nil, err
}
srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
dstAddr := &net.UDPAddr{
IP: ip,
Port: int(port),
}
return net.DialUDP(trackerUrl.Scheme, srcAddr, dstAddr)
}
func connect(conn *net.UDPConn, timeout int64) (response *UdpConnectResponse, err error) {
request := NewUdpConnectRequest()
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)))
conn.Write(buf)
@@ -217,27 +199,24 @@ func connect(conn *net.UDPConn, timeout int64) (response *UdpConnectResponse, er
err = NewTrackerError(ErrResponse, errors.New("invalid response"))
return
}
response = NewUdpConnectResponse(buf)
response = newUdpConnectResponse(buf)
return
}
func announce(conn *net.UDPConn, timeout int64, connectionId uint64, metaInfo *MetaInfo) (response *UdpAnnounceResponse, err error) {
request := NewUdpAnnounceRequest(connectionId)
request.InfoHash = metaInfo.InfoHash
peerID, err := GenPeerID()
if err != nil {
fmt.Println(err)
return
}
request.PeerID = peerID
request.Downloaded = 0
request.Uploaded = 0
request.Left = metaInfo.GetTotalSize() - request.Downloaded
request.Event = 0
request.IP = 0
request.Key = 0
request.NumWant = 50
request.Port = 6882
func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) {
torrent := tracker.torrent
request := newUdpAnnounceRequest(connectionId)
request.infoHash = torrent.MetaInfo.InfoHash
request.peerID = torrent.client.PeerID
request.downloaded = 0
request.uploaded = 0
request.left = torrent.MetaInfo.FileSize - request.downloaded
request.event = 0
request.ip = 0
request.key = 0
request.numWant = -1
request.port = 6882
encode := request.encode()
conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout)))
conn.Write(encode)
@@ -247,16 +226,29 @@ func announce(conn *net.UDPConn, timeout int64, connectionId uint64, metaInfo *M
fmt.Println(err)
return
}
response = NewUdpAnnounceResponse(buf[:len])
response = newUdpAnnounceResponse(buf[:len])
return
}
func (metaInfo *MetaInfo) Tracker() (peers []Peer, err error) {
if len(metaInfo.AnnounceList) > 0 {
func (tracker *Tracker) Tracker() (peers []Peer, err error) {
metaInfo := tracker.torrent.MetaInfo
checkAnnounceList := func(announceList [][]string) bool {
if len(announceList) > 0 {
for _, l := range announceList {
if len(l) > 0 {
return true
}
}
}
return false
}
if checkAnnounceList(metaInfo.AnnounceList) {
for _, announceArr := range metaInfo.AnnounceList {
if len(announceArr) > 0 {
for _, announce := range announceArr {
peers, err = doTracker(announce, metaInfo)
peers, err = tracker.doTracker(announce)
if err == nil {
return
}
@@ -264,7 +256,7 @@ func (metaInfo *MetaInfo) Tracker() (peers []Peer, err error) {
}
}
} else {
peers, err = doTracker(metaInfo.Announce, metaInfo)
peers, err = tracker.doTracker(metaInfo.Announce)
if err == nil {
return
}
@@ -272,16 +264,16 @@ func (metaInfo *MetaInfo) Tracker() (peers []Peer, err error) {
return
}
func doTracker(announce string, metaInfo *MetaInfo) (peers []Peer, err error) {
func (tracker *Tracker) doTracker(announce string) (peers []Peer, err error) {
if announce != "" {
url, _ := url.Parse(announce)
switch url.Scheme {
case "http", "https":
// TODO UDP test
break
return httpTracker(url, metaInfo)
return tracker.httpTracker(url)
case "udp", "udp4", "udp6":
return udpTracker(url, metaInfo)
return tracker.udpTracker(url)
default:
return nil, errors.New("unsupported protocol")
}
@@ -289,31 +281,18 @@ func doTracker(announce string, metaInfo *MetaInfo) (peers []Peer, err error) {
return nil, errors.New("empty announce")
}
// 生成Peer ID,规则为前三位固定字母(-GP)+SemVer(xyz),后面随机生成
// 参考:https://wiki.theory.org/index.php/BitTorrentSpecification#peer_id
func GenPeerID() ([20]byte, error) {
peerId := [20]byte{'-', 'G', 'P', '0', '0', '1'}
_, err := rand.Read(peerId[6:])
if err != nil {
return peerId, err
}
return peerId, nil
}
// http://bittorrent.org/beps/bep_0003.html#trackers
func httpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
func (tracker *Tracker) httpTracker(url *url.URL) (peers []Peer, err error) {
metaInfo := tracker.torrent.MetaInfo
peerID := tracker.torrent.client.PeerID
query := url.Query()
query.Add("info_hash", string(metaInfo.InfoHash[:]))
// Generate peer ID
peerID, err := GenPeerID()
if err != nil {
return
}
query.Add("peer_id", string(peerID[:]))
query.Add("port", "6882")
query.Add("uploaded", "0")
query.Add("downloaded", "0")
query.Add("left", strconv.FormatInt(int64(metaInfo.GetTotalSize()), 10))
query.Add("left", strconv.FormatInt(int64(metaInfo.FileSize), 10))
url.RawQuery = query.Encode()
request, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
@@ -340,14 +319,15 @@ func httpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
}
// http://bittorrent.org/beps/bep_0015.html
func udpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
func (tracker *Tracker) udpTracker(url *url.URL) (peers []Peer, err error) {
conn, err := dial(url)
if err != nil {
return
}
defer conn.Close()
announceResponse, err := func() (announceResponse *UdpAnnounceResponse, err error) {
var connectResponse *UdpConnectResponse
announceResponse, err := func() (announceResponse *udpAnnounceResponse, err error) {
var connectResponse *udpConnectResponse
// 0:connect 1:announce
state := 0
// 访问超时最多重试8次,当有请求成功则将重试次数重置为0
@@ -356,7 +336,7 @@ func udpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
timeout := int64(udpConnectTimeout * math.Pow(2, float64(n)))
switch state {
case 0:
connectResponse, err = connect(conn, timeout)
connectResponse, err = tracker.connect(conn, timeout)
if err != nil {
if oe, ok := err.(*net.OpError); ok && oe.Timeout() {
break
@@ -368,7 +348,7 @@ func udpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
state = 1
break
case 1:
announceResponse, err = announce(conn, timeout, connectResponse.ConnectionId, metaInfo)
announceResponse, err = tracker.announce(conn, timeout, connectResponse.connectionId)
if err != nil {
if oe, ok := err.(*net.OpError); ok && oe.Timeout() {
break
@@ -386,7 +366,29 @@ func udpTracker(url *url.URL, metaInfo *MetaInfo) (peers []Peer, err error) {
return
}
return announceResponse.Peers, nil
return announceResponse.peers, nil
}
func dial(trackerUrl *url.URL) (*net.UDPConn, error) {
host, portStr, err := net.SplitHostPort(trackerUrl.Host)
if err != nil {
return nil, err
}
port, err := strconv.ParseInt(portStr, 10, 32)
if err != nil {
return nil, err
}
ip, err := lookupIP(host, trackerUrl.Scheme)
if err != nil {
return nil, err
}
srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
dstAddr := &net.UDPAddr{
IP: ip,
Port: int(port),
}
return net.DialUDP(trackerUrl.Scheme, srcAddr, dstAddr)
}
func lookupIP(host string, scheme string) (ip net.IP, err error) {
+8
View File
@@ -122,6 +122,14 @@ func TestTemp(t *testing.T) {
s = time.Now().UnixNano()
f.Write(bts)
fmt.Printf("write from 128M+9b use %d\n", time.Now().UnixNano()-s)
s = time.Now().UnixNano()
f.Seek(0, 0)
f.Write(bts)
fmt.Printf("write from 0 agein use %d\n", time.Now().UnixNano()-s)
s = time.Now().UnixNano()
f.Seek(1024*1024*64, 0)
f.Write(bts)
fmt.Printf("write from agein 64M use %d\n", time.Now().UnixNano()-s)
}
func fileMd5(filePath string) string {
+4 -1
View File
@@ -2,4 +2,7 @@ module gopeed
go 1.12
require github.com/marksamman/bencode v0.0.0-20150821143521-dc84f26e086e
require (
github.com/cenkalti/mse v0.0.0-20140930130441-6ef65f170972
github.com/marksamman/bencode v0.0.0-20150821143521-dc84f26e086e
)
+2
View File
@@ -1,2 +1,4 @@
github.com/cenkalti/mse v0.0.0-20140930130441-6ef65f170972 h1:FvAasVrPm/VUO8M0odLDqA9Y7VbALYq3PRAwmWGPw78=
github.com/cenkalti/mse v0.0.0-20140930130441-6ef65f170972/go.mod h1:wGf99xKTJ8f/a8cC1inMqFz9cGEzISLLid5F6i5fI/g=
github.com/marksamman/bencode v0.0.0-20150821143521-dc84f26e086e h1:KMs6SK8iDSR1+ZzOK10L5wGPpWDByyvOe5nrqk51g2U=
github.com/marksamman/bencode v0.0.0-20150821143521-dc84f26e086e/go.mod h1:+AHfJo5+69p+fjvMJTmYajNP9rFBHQcaTDFmuXRRATI=