Files
gopeed/protocol/bt/torrent/torrent.go
T
2020-03-03 10:08:39 +08:00

84 lines
1.9 KiB
Go

package torrent
import (
"errors"
"time"
"github.com/monkeyWie/gopeed/protocol/bt/metainfo"
log "github.com/sirupsen/logrus"
)
type Torrent struct {
PeerID [20]byte
MetaInfo *metainfo.MetaInfo
pieceStates *pieceStates
peerPool *peerPool
Path string
}
func NewTorrent(peerID [20]byte, metaInfo *metainfo.MetaInfo) *Torrent {
torrent := &Torrent{
PeerID: peerID,
MetaInfo: metaInfo,
pieceStates: newPiecesState(metaInfo),
}
return torrent
}
func (t *Torrent) Download(path string) {
t.Path = path
t.peerPool = newPeerPool(t)
t.peerPool.fetch()
pieceQueueCh := make(chan interface{}, 128)
defer close(pieceQueueCh)
downloadedCh := make(chan interface{})
defer close(downloadedCh)
for i := 0; i < len(t.MetaInfo.Info.Pieces); i++ {
// 加入下载队列
pieceQueueCh <- nil
go func(index int) {
t.pieceStates.setState(index, stateDownloading)
// 一直下载到成功
for {
peer := t.peerPool.get()
if peer == nil {
time.Sleep(time.Second * 10)
continue
}
pc := newPeerConn(t, peer)
err := pc.ready()
if err != nil {
t.peerPool.unavailable(peer)
} else {
err = pc.downloadPiece(index)
if err != nil {
log.Debugf("piece %d download fail:%s", index, err.Error())
// 下载失败
t.peerPool.unavailable(peer)
if errors.Is(err, errPieceCheckFailed) {
// piece校验失败,需要重新下载过一遍,清空block记录
t.pieceStates.clearBlocks(index)
}
continue
} else {
// 下载完成
t.pieceStates.setState(index, stateFinish)
t.peerPool.release(peer)
log.Debugf("piece %d download success,total:%d,left:%d", index, t.pieceStates.size(), t.pieceStates.getLeft())
if t.pieceStates.isDone() {
downloadedCh <- nil
}
break
}
}
}
<-pieceQueueCh
}(i)
}
<-downloadedCh
log.Debug("download end")
}