This commit is contained in:
liwei committed 2019-09-12 18:57:17 +08:00
1 parent 075852b847
commit adb7d89018
19 files changed
+415 -258

No files matched your search

+2 -2
View File
@@ -2,8 +2,8 @@ package bt
import (
"crypto/rand"
"gopeed/down/bt/metainfo"
"gopeed/down/bt/torrent"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/torrent"
"net/url"
)
+15 -3
View File
@@ -39,8 +39,8 @@ type File struct {
}
type ExtraInfo struct {
InfoHash [20]byte
FileSize uint64
InfoHash [20]byte
TotalSize uint64
}
func ParseFromFile(path string) (*MetaInfo, error) {
@@ -75,10 +75,22 @@ func ParseFromFile(path string) (*MetaInfo, error) {
}
}
}
metaInfo.FileSize = getTotalSize(&metaInfo)
metaInfo.TotalSize = getTotalSize(&metaInfo)
return &metaInfo, nil
}
// 获取某个分片的大小
func (metaInfo *MetaInfo) GetPieceSize(index int) uint64 {
// 是否为最后一个分片
if index == len(metaInfo.Info.Pieces)-1 {
size := metaInfo.TotalSize % metaInfo.Info.PieceLength
if size > 0 {
return size
}
}
return metaInfo.Info.PieceLength
}
func getTotalSize(metaInfo *MetaInfo) uint64 {
if metaInfo.Info != nil {
if len(metaInfo.Info.Files) > 0 {
@@ -1,31 +1,52 @@
package peer
package message
import (
"encoding/binary"
)
type MessageID int
// message ID
type ID byte
const (
Keepalive MessageID = -1
Choke MessageID = 0
Unchoke MessageID = 1
Interested MessageID = 2
NotInterested MessageID = 3
Have MessageID = 4
Bitfield MessageID = 5
Request MessageID = 6
Piece MessageID = 7
Cancel MessageID = 8
IdChoke ID = 0
IdUnchoke ID = 1
IdInterested ID = 2
IdNotinterested ID = 3
IdHave ID = 4
IdBitfield ID = 5
IdRequest ID = 6
IdPiece ID = 7
IdCancel ID = 8
)
type Message struct {
/*type Message struct {
Length uint32
ID MessageID
Payload []byte
}*/
type Message struct {
Length uint32
ID ID
}
func NewMessage(id MessageID, payload []byte) *Message {
func (msg Message) Encode() []byte {
buf := make([]byte, 5)
binary.BigEndian.PutUint32(buf, msg.Length)
buf[4] = byte(msg.ID)
return buf
}
func (msg Message) Decode(buf []byte) {
msg.Length = binary.BigEndian.Uint32(buf)
msg.ID = ID(buf[4])
}
type Serialize interface {
Encode() []byte
Decode(buf []byte) Serialize
}
/*func NewMessage(id MessageID, payload []byte) *Message {
message := &Message{ID: id, Payload: payload}
if id == Keepalive {
message.Length = 0
@@ -57,7 +78,7 @@ func (msg *Message) Decode(buf []byte) {
msg.Payload = buf[5:]
}
}
}
}*/
// Message protocol
// Length prefix| message ID | Payload
@@ -73,39 +94,3 @@ func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error
}
return
}
type MsgBitfield []byte
// 某个分片是否下载完成
func (mb MsgBitfield) IsComplete(i int) bool {
bts := []byte(mb)
index := i / 8
if index >= len(bts) {
return false
}
return bts[index]&(1<<uint(7-i%8)) > 0
}
// 给定一组分片下载状态,计算出当前peer能提供下载的分片下标
func (mb MsgBitfield) Have(pieces []bool) []int {
arr := make([]int, 0)
bts := []byte(mb)
length := len(pieces) / 8
if len(pieces)%8 != 0 {
length++
}
for i := 0; i < length; i++ {
for j := 0; j < 8; j++ {
index := i*8 + j
// 如果此分片在本地还未下载,检查peer是否能提供该分片下载
if index < len(pieces) &&
!pieces[index] &&
i < len(bts) &&
uint(bts[i])&(1<<uint(7-j)) > 0 {
arr = append(arr, index)
}
}
}
return arr
}
+59
View File
@@ -0,0 +1,59 @@
package message
// bitfield: <len=0001+X><id=5><bitfield>
type Bitfield struct {
Message
payload []byte
}
func NewBitfield(payload []byte) *Bitfield {
return &Bitfield{
Message: Message{
Length: uint32(len(payload) + 1),
ID: IdBitfield,
},
payload: payload,
}
}
func (b *Bitfield) Encode() []byte {
return append(b.Message.Encode(), b.payload...)
}
func (b *Bitfield) Decode(buf []byte) Serialize {
b.Message.Decode(buf)
copy(b.payload, buf[5:])
return b
}
// 某个分片是否下载完成
func (b *Bitfield) IsComplete(i int) bool {
index := i / 8
if index >= len(b.payload) {
return false
}
return b.payload[index]&(1<<(7-i%8)) > 0
}
// 给定一组分片下载状态,计算出当前peer能提供下载的分片下标
func (b *Bitfield) Have(pieces []bool) []int {
arr := make([]int, 0)
length := len(pieces) / 8
if len(pieces)%8 != 0 {
length++
}
for i := 0; i < length; i++ {
for j := 0; j < 8; j++ {
index := i*8 + j
// 如果此分片在本地还未下载,检查peer是否能提供该分片下载
if index < len(pieces) &&
!pieces[index] &&
i < len(b.payload) &&
b.payload[i]&(1<<(7-j)) > 0 {
arr = append(arr, index)
}
}
}
return arr
}
+24
View File
@@ -0,0 +1,24 @@
package message
// choke: <len=0001><id=0>
type Choke struct {
Message
}
func NewChoke() *Choke {
return &Choke{
Message{
Length: 1,
ID: IdChoke,
},
}
}
func (c *Choke) Encode() []byte {
return c.Message.Encode()
}
func (c *Choke) Decode(buf []byte) Serialize {
c.Message.Decode(buf)
return c
}
+24
View File
@@ -0,0 +1,24 @@
package message
// choke: <len=0001><id=0>
type Interested struct {
Message
}
func NewInterested() *Interested {
return &Interested{
Message{
Length: 1,
ID: IdInterested,
},
}
}
func (c *Interested) Encode() []byte {
return c.Message.Encode()
}
func (c *Interested) Decode(buf []byte) Serialize {
c.Message.Decode(buf)
return c
}
+12
View File
@@ -0,0 +1,12 @@
package message
// keep-alive: <len=0000>
type Keepalive struct{}
func (k *Keepalive) Encode() []byte {
return []byte{0, 0, 0, 0}
}
func (k *Keepalive) Decode(buf []byte) Serialize {
return &Keepalive{}
}
@@ -0,0 +1,24 @@
package message
// choke: <len=0001><id=0>
type NotInterested struct {
Message
}
func NewNotInterested() *NotInterested {
return &NotInterested{
Message{
Length: 1,
ID: IdNotinterested,
},
}
}
func (ni *NotInterested) Encode() []byte {
return ni.Message.Encode()
}
func (ni *NotInterested) Decode(buf []byte) Serialize {
ni.Message.Decode(buf)
return ni
}
+23
View File
@@ -0,0 +1,23 @@
package message
import "io"
// piece: <len=0009+X><id=7><index><begin><block>
type Piece struct {
Message
Index uint32
Begin uint32
Block io.Reader
}
func newPiece(index uint32, begin uint32, reader io.Reader, length uint32) *Piece {
return &Piece{
Message: Message{
Length: 9 + length,
ID: IdPiece,
},
Index: index,
Begin: begin,
Block: io.LimitReader(reader, int64(length)),
}
}
+41
View File
@@ -0,0 +1,41 @@
package message
import (
"encoding/binary"
)
// request: <len=0013><id=6><index><begin><length>
type Request struct {
Message
Index uint32
Begin uint32
Length uint32
}
func NewRequest(index uint32, begin uint32, length uint32) *Request {
return &Request{
Message: Message{
Length: 13,
ID: IdRequest,
},
Index: index,
Begin: begin,
Length: length,
}
}
func (r *Request) Encode() []byte {
buf := make([]byte, 12)
binary.BigEndian.PutUint32(buf[:4], r.Index)
binary.BigEndian.PutUint32(buf[4:8], r.Begin)
binary.BigEndian.PutUint32(buf[8:], r.Length)
return append(r.Message.Encode(), buf...)
}
func (r *Request) Decode(buf []byte) Serialize {
r.Message.Decode(buf)
r.Length = binary.BigEndian.Uint32(buf[5:9])
r.Begin = binary.BigEndian.Uint32(buf[9:13])
r.Index = binary.BigEndian.Uint32(buf[13:17])
return r
}
+24
View File
@@ -0,0 +1,24 @@
package message
// unchoke: <len=0001><id=1>
type Unchoke struct {
Message
}
func NewUnchoke() *Unchoke {
return &Unchoke{
Message{
Length: 1,
ID: IdUnchoke,
},
}
}
func (u *Unchoke) Encode() []byte {
return u.Message.Encode()
}
func (u *Unchoke) Decode(buf []byte) Serialize {
u.Message.Decode(buf)
return u
}
-138
View File
@@ -1,138 +0,0 @@
package peer
import (
"fmt"
"reflect"
"strconv"
"testing"
)
func TestMsgBitfield_IsComplete(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.IsComplete(tt.args.i); got != tt.want {
t.Errorf("MsgBitfield.get() = %v, want %v", got, tt.want)
}
})
}
}
func TestMsgBitfield_Have(t *testing.T) {
type args struct {
pieces []bool
}
tests := []struct {
name string
mb MsgBitfield
args args
want []int
}{
{
"case-1",
MsgBitfield([]byte{convertByte("11111111"), convertByte("00000000")}),
args{
[]bool{false, false, false, false, false, false, false, false},
},
[]int{0, 1, 2, 3, 4, 5, 6, 7},
},
{
"case-2",
MsgBitfield([]byte{convertByte("01010101"), convertByte("01000000")}),
args{
[]bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
},
[]int{1, 3, 5, 7, 9},
},
{
"case-3",
MsgBitfield([]byte{convertByte("11010101"), convertByte("01000000")}),
args{
[]bool{false, true, false},
},
[]int{0},
},
{
"case-4",
MsgBitfield([]byte{convertByte("11010101")}),
args{
[]bool{false, true, false, false, true, false, false, true, false},
},
[]int{0, 3, 5},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.mb.Have(tt.args.pieces); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MsgBitfield.Have() = %v, want %v", got, tt.want)
}
})
}
}
func TestMsg(t *testing.T) {
fmt.Println(Choke)
fmt.Println(Bitfield)
}
func convertByte(bit string) byte {
i, _ := strconv.ParseUint(bit, 2, 8)
return byte(i)
}
+88 -52
View File
@@ -5,8 +5,10 @@ import (
"encoding/binary"
"fmt"
"github.com/cenkalti/mse"
"gopeed/down/bt/peer"
"github.com/monkeyWie/gopeed/down/bt/peer"
"github.com/monkeyWie/gopeed/down/bt/peer/message"
"io"
"math"
"net"
)
@@ -23,7 +25,8 @@ type peerState struct {
// peer is interested in this client
peerInterested bool
bitfield peer.MsgBitfield
writeMsgCh chan []byte
bitfield *message.Bitfield
}
// 使用MSE加密来避免运营商对bt流量的封锁,基本上现在市面上BT客户端都默认开启了,不用MSE的话很多Peer拒绝连接
@@ -81,38 +84,50 @@ func (ps *peerState) handshake() (*peer.Handshake, error) {
}
func (ps *peerState) download() error {
// 异步写,防止读取阻塞
ps.writeMsgCh = make(chan []byte, 64)
go func() {
for buf := range ps.writeMsgCh {
_, err := ps.conn.Write(buf)
if err != nil {
fmt.Println(err)
ps.conn.Close()
}
}
}()
scanner := bufio.NewScanner(ps.conn)
scanner.Split(peer.SplitMessage)
scanner.Split(message.SplitMessage)
for scanner.Scan() {
buf := scanner.Bytes()
message := &peer.Message{}
message.Decode(buf)
switch message.ID {
case peer.Keepalive:
break
case peer.Choke:
ps.peerChoking = true
break
case peer.Unchoke:
ps.handleUnchoke()
break
case peer.Interested:
ps.peerInterested = true
break
case peer.NotInterested:
ps.peerInterested = false
break
case peer.Have:
break
case peer.Bitfield:
ps.handleBitfield(message.Payload)
break
case peer.Request:
break
case peer.Piece:
break
case peer.Cancel:
break
length := binary.BigEndian.Uint32(buf[:4])
if length == 0 {
// keepalive
} else {
messageID := message.ID(buf[4])
payload := buf[5:]
switch messageID {
case message.IdChoke:
break
case message.IdUnchoke:
ps.handleUnchoke()
break
case message.IdInterested:
break
case message.IdNotinterested:
break
case message.IdHave:
break
case message.IdBitfield:
ps.handleBitfield(payload)
break
case message.IdRequest:
break
case message.IdPiece:
ps.handlePiece()
break
case message.IdCancel:
break
}
}
}
return nil
@@ -124,16 +139,19 @@ func (ps *peerState) handleUnchoke() {
if ps.amInterested {
have := ps.getHavePieces(ps.bitfield)
if len(have) > 0 {
for i := range have {
buf := make([]byte, 12)
binary.BigEndian.PutUint32(buf[:4], uint32(have[i]))
binary.BigEndian.PutUint32(buf[4:8], 0)
binary.BigEndian.PutUint32(buf[8:], 2<<13)
_, err := ps.conn.Write(peer.NewMessage(peer.Request, buf).Encode())
if err != nil {
ps.conn.Close()
return
// 获取分片的长度
for _, index := range have {
pieceLength := ps.torrent.MetaInfo.GetPieceSize(index)
lastOffset := uint64(0)
// 按块下载分片
for lastOffset < pieceLength {
blockLength := math.Min(2<<13, float64(pieceLength-lastOffset))
ps.write(message.NewRequest(uint32(index), uint32(lastOffset), uint32(blockLength)).Encode())
lastOffset += uint64(blockLength)
fmt.Println("request piece")
}
// TODO 测试代码
break
}
return
}
@@ -142,32 +160,50 @@ func (ps *peerState) handleUnchoke() {
}
func (ps *peerState) handleBitfield(payload []byte) {
ps.bitfield = payload
ps.bitfield = message.NewBitfield(payload)
have := ps.getHavePieces(ps.bitfield)
if len(have) > 0 {
// 表示对该peer感兴趣,并且不choked该peer
_, err := ps.conn.Write(peer.NewMessage(peer.Interested, []byte{}).Encode())
if err != nil {
ps.conn.Close()
return
}
ps.write(message.NewInterested().Encode())
ps.amInterested = true
_, err = ps.conn.Write(peer.NewMessage(peer.Unchoke, []byte{}).Encode())
if err != nil {
ps.conn.Close()
return
}
ps.write(message.NewUnchoke().Encode())
ps.amChoking = false
} else {
ps.conn.Close()
}
}
// 处理分片下载响应
func (ps *peerState) handlePiece() {
fmt.Println("one piece")
}
// 获取peer能提供需要下载的文件分片
func (ps *peerState) getHavePieces(bitfield peer.MsgBitfield) []int {
func (ps *peerState) getHavePieces(bitfield *message.Bitfield) []int {
states := make([]bool, len(ps.torrent.PieceState))
for i := range states {
states[i] = ps.torrent.PieceState[i].complete
}
return bitfield.Have(states)
}
// 异步写入数据
func (ps *peerState) write(buf []byte) {
ps.writeMsgCh <- buf
}
// 获取要写入到的文件
/*func (ps *peerState) getWriteFile(request *message.Request) string {
info := ps.torrent.MetaInfo.Info
// 单文件
if len(info.Files) == 0 {
return info.Name
} else {
request.Index * info.PieceLength + be
for i := 0; i < len(info.Files); i++ {
}
}
}
*/
+9 -3
View File
@@ -2,9 +2,9 @@ package torrent
import (
"fmt"
"gopeed/down/bt/metainfo"
"gopeed/down/bt/peer"
"gopeed/down/bt/tracker"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/peer"
"github.com/monkeyWie/gopeed/down/bt/tracker"
"testing"
)
@@ -31,6 +31,8 @@ func Test_peerState_download(t *testing.T) {
if err != nil {
panic(err)
}
fmt.Println("handshake end")
// 开始下载
err = ps.download()
@@ -77,3 +79,7 @@ func peerTest(torrent *Torrent, peer *peer.Peer, ch chan *peerState) {
}
ch <- ps
}
func TestSome(t *testing.T) {
fmt.Println(11)
}
+1 -1
View File
@@ -1,7 +1,7 @@
package torrent
import (
"gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
)
type Torrent struct {
+4 -4
View File
@@ -4,8 +4,8 @@ import (
"encoding/binary"
"errors"
"fmt"
"gopeed/down/bt/metainfo"
"gopeed/down/bt/peer"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/peer"
"io"
"io/ioutil"
"math"
@@ -208,7 +208,7 @@ func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId
request.peerID = tracker.PeerID
request.downloaded = 0
request.uploaded = 0
request.left = tracker.MetaInfo.FileSize - request.downloaded
request.left = tracker.MetaInfo.TotalSize - request.downloaded
request.event = 0
request.ip = 0
request.key = 0
@@ -287,7 +287,7 @@ func (tracker *Tracker) httpTracker(url *url.URL) (peers []peer.Peer, err error)
query.Add("port", "6882")
query.Add("uploaded", "0")
query.Add("downloaded", "0")
query.Add("left", strconv.FormatInt(int64(metaInfo.FileSize), 10))
query.Add("left", strconv.FormatInt(int64(metaInfo.TotalSize), 10))
url.RawQuery = query.Encode()
request, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
+1 -1
View File
@@ -2,7 +2,7 @@ package tracker
import (
"fmt"
"gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
"testing"
)
+2 -2
View File
@@ -1,6 +1,6 @@
module gopeed
module github.com/monkeyWie/gopeed
go 1.12
go 1.13
require (
github.com/cenkalti/mse v0.0.0-20140930130441-6ef65f170972
+26 -1
View File
@@ -2,7 +2,11 @@ package main
import (
"fmt"
"gopeed/down/http"
"github.com/monkeyWie/gopeed/down/bt/metainfo"
"github.com/monkeyWie/gopeed/down/bt/peer"
"github.com/monkeyWie/gopeed/down/bt/torrent"
"github.com/monkeyWie/gopeed/down/bt/tracker"
"github.com/monkeyWie/gopeed/down/http"
)
func main() {
@@ -20,5 +24,26 @@ func main() {
}
got, _ := http.Resolve(request)
fmt.Println(got)
getUsablePeer()
// webview.Open("Minimal webview example", "https://www.baidu.com", 800, 600, true)
}
func buildTorrent() *torrent.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 torrent.NewTorrent(peer.GenPeerID(), metaInfo)
}
// 获取一个能连接上的peer
func getUsablePeer() {
torrent := buildTorrent()
tracker := &tracker.Tracker{
PeerID: torrent.PeerID,
MetaInfo: torrent.MetaInfo,
}
tracker.Tracker()
}