mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-15 21:03:41 +00:00
bitfield handle
This commit is contained in:
1 parent
544e8bc7b1
commit
d6c88d0c4b
7 files changed
+216
-423
No files matched your search
+76
-42
@@ -4,32 +4,66 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageType int
|
type MessageID byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Keepalive MessageType = -1
|
Keepalive MessageID = -1
|
||||||
Choke MessageType = iota
|
Choke MessageID = 0
|
||||||
Unchoke
|
Unchoke MessageID = 1
|
||||||
Interested
|
Interested MessageID = 2
|
||||||
NotInterested
|
NotInterested MessageID = 3
|
||||||
Have
|
Have MessageID = 4
|
||||||
Bitfield
|
Bitfield MessageID = 5
|
||||||
Request
|
Request MessageID = 6
|
||||||
Piece
|
Piece MessageID = 7
|
||||||
Cancel
|
Cancel MessageID = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 {
|
type Message struct {
|
||||||
Length uint32
|
Length uint32
|
||||||
Type int
|
ID MessageID
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMessage(id MessageID, payload []byte) *Message {
|
||||||
|
message := &Message{ID: id, Payload: payload}
|
||||||
|
if id == Keepalive {
|
||||||
|
message.Length = 0
|
||||||
|
} else {
|
||||||
|
message.Length = uint32(len(payload) + 1)
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Message) Encode() []byte {
|
||||||
|
if msg.ID == Keepalive {
|
||||||
|
return make([]byte, 4)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, 5+len(msg.Payload))
|
||||||
|
binary.BigEndian.PutUint32(buf, msg.Length)
|
||||||
|
buf[4] = byte(msg.ID)
|
||||||
|
buf = append(buf, msg.Payload[:]...)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Message) Decode(buf []byte) {
|
||||||
|
msg.Length = binary.BigEndian.Uint32(buf)
|
||||||
|
if msg.Length == 0 {
|
||||||
|
msg.ID = -1
|
||||||
|
} else {
|
||||||
|
msg.ID = MessageID(buf[4])
|
||||||
|
if msg.Length > 4 {
|
||||||
|
msg.Payload = buf[5:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
if !atEOF && len(data) > 4 {
|
if !atEOF && len(data) > 4 {
|
||||||
length := int(binary.BigEndian.Uint32(data))
|
length := int(binary.BigEndian.Uint32(data))
|
||||||
@@ -40,34 +74,10 @@ func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error
|
|||||||
return
|
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
|
type MsgBitfield []byte
|
||||||
|
|
||||||
// 某个分片是否下载完成
|
// 某个分片是否下载完成
|
||||||
func (mb MsgBitfield) get(i int) bool {
|
func (mb MsgBitfield) IsComplete(i int) bool {
|
||||||
bts := []byte(mb)
|
bts := []byte(mb)
|
||||||
index := i / 8
|
index := i / 8
|
||||||
if index >= len(bts) {
|
if index >= len(bts) {
|
||||||
@@ -75,3 +85,27 @@ func (mb MsgBitfield) get(i int) bool {
|
|||||||
}
|
}
|
||||||
return bts[index]&(1<<uint(7-i%8)) > 0
|
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
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMsgBitfield_get(t *testing.T) {
|
func TestMsgBitfield_IsComplete(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
i int
|
i int
|
||||||
}
|
}
|
||||||
@@ -66,13 +68,70 @@ func TestMsgBitfield_get(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := tt.mb.get(tt.args.i); got != tt.want {
|
if got := tt.mb.IsComplete(tt.args.i); got != tt.want {
|
||||||
t.Errorf("MsgBitfield.get() = %v, want %v", 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 {
|
func convertByte(bit string) byte {
|
||||||
i, _ := strconv.ParseUint(bit, 2, 8)
|
i, _ := strconv.ParseUint(bit, 2, 8)
|
||||||
return byte(i)
|
return byte(i)
|
||||||
|
|||||||
@@ -1,316 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"github.com/cenkalti/mse"
|
|
||||||
"gopeed/down/bt/metainfo"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageType byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
ProtocolIdentifier = "BitTorrent protocol"
|
|
||||||
ProtocolIdentifierLength = 0x13
|
|
||||||
|
|
||||||
Choke MessageType = iota
|
|
||||||
Unchoke
|
|
||||||
Interested
|
|
||||||
NotInterested
|
|
||||||
Have
|
|
||||||
Bitfield
|
|
||||||
Request
|
|
||||||
Piece
|
|
||||||
Cancel
|
|
||||||
)
|
|
||||||
|
|
||||||
type Peer struct {
|
|
||||||
IP uint32
|
|
||||||
Port uint16
|
|
||||||
|
|
||||||
Torrent *Torrent
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handshake struct {
|
|
||||||
Pstrlen byte
|
|
||||||
Pstr [19]byte
|
|
||||||
Reserved [8]byte
|
|
||||||
InfoHash [20]byte
|
|
||||||
PeerID [20]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Length uint32
|
|
||||||
Type byte
|
|
||||||
Payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessageBitfield []bool
|
|
||||||
|
|
||||||
func decodeBitfield(payload []byte) MessageBitfield {
|
|
||||||
bits := make([]bool, len(payload)*8)
|
|
||||||
for i, b := range payload {
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
bits[i*8+j] = b&(1<<uint(7-j)) > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bits
|
|
||||||
}
|
|
||||||
|
|
||||||
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, error) {
|
|
||||||
writer := new(bytes.Buffer)
|
|
||||||
err := binary.Write(writer, binary.BigEndian, handshake)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return writer.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := dialMse(peer.Address(), peer.Torrent.MetaInfo.InfoHash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = peer.doHandshake(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// handshake success,init status
|
|
||||||
peer.AmChoking = true
|
|
||||||
peer.AmInterested = false
|
|
||||||
peer.PeerChoking = true
|
|
||||||
peer.PeerInterested = false
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
scanner.Split(splitMessage)
|
|
||||||
for scanner.Scan() {
|
|
||||||
buf := scanner.Bytes()
|
|
||||||
message := decodeMessage(buf)
|
|
||||||
// keep-alive message
|
|
||||||
if message.Length == 0 {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
switch MessageType(buf[4]) {
|
|
||||||
case Choke:
|
|
||||||
peer.PeerChoking = true
|
|
||||||
break
|
|
||||||
case Unchoke:
|
|
||||||
peer.PeerChoking = false
|
|
||||||
break
|
|
||||||
case Interested:
|
|
||||||
peer.PeerInterested = true
|
|
||||||
break
|
|
||||||
case NotInterested:
|
|
||||||
peer.PeerInterested = false
|
|
||||||
break
|
|
||||||
case Have:
|
|
||||||
break
|
|
||||||
case Bitfield:
|
|
||||||
bitfield := decodeBitfield(message.Payload)
|
|
||||||
fmt.Println(bitfield)
|
|
||||||
break
|
|
||||||
case Request:
|
|
||||||
break
|
|
||||||
case Piece:
|
|
||||||
break
|
|
||||||
case Cancel:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*var (
|
|
||||||
// This client is choking the peer
|
|
||||||
amChocking = true
|
|
||||||
// This client is interested in the peer
|
|
||||||
amInterested = false
|
|
||||||
// Peer is choking this client
|
|
||||||
peerChocking = true
|
|
||||||
// Peer is interested in this client
|
|
||||||
peerInterested = false
|
|
||||||
)
|
|
||||||
|
|
||||||
// Receive message
|
|
||||||
var cacheBuf []byte
|
|
||||||
buf := make([]byte, 8192)
|
|
||||||
for {
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if n > 0 {
|
|
||||||
if len(cacheBuf) > 0 {
|
|
||||||
buf = append(cacheBuf, buf...)
|
|
||||||
}
|
|
||||||
msg, err := decodeMessage(buf, n)
|
|
||||||
if err != nil {
|
|
||||||
if err == NMD {
|
|
||||||
cacheBuf = make([]byte, n)
|
|
||||||
copy(cacheBuf, buf[0:n])
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msgLength := binary.BigEndian.Uint32(buf)
|
|
||||||
// keep-alive message
|
|
||||||
if msgLength == 0 {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
switch MessageType(buf[4]) {
|
|
||||||
case Choke:
|
|
||||||
peerChocking = true
|
|
||||||
break
|
|
||||||
case Unchoke:
|
|
||||||
peerChocking = false
|
|
||||||
break
|
|
||||||
case Interested:
|
|
||||||
peerInterested = true
|
|
||||||
break
|
|
||||||
case NotInterested:
|
|
||||||
peerInterested = false
|
|
||||||
break
|
|
||||||
case Have:
|
|
||||||
break
|
|
||||||
case Bitfield:
|
|
||||||
break
|
|
||||||
case Request:
|
|
||||||
break
|
|
||||||
case Piece:
|
|
||||||
break
|
|
||||||
case Cancel:
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handshake of Peer wire protocol
|
|
||||||
// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake
|
|
||||||
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)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
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 decodeMessage(buf []byte) *Message {
|
|
||||||
msg := &Message{}
|
|
||||||
msg.Length = binary.BigEndian.Uint32(buf)
|
|
||||||
// keep-alive message
|
|
||||||
if msg.Length > 0 {
|
|
||||||
msg.Type = buf[4]
|
|
||||||
msg.Payload = buf[5:]
|
|
||||||
}
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func doBitfield(peer *Peer, metaInfo *metainfo.MetaInfo, conn net.Conn) {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_decodeBitfield(t *testing.T) {
|
|
||||||
fmt.Printf("%08b", 0xFF)
|
|
||||||
type args struct {
|
|
||||||
payload []byte
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want MessageBitfield
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"case-11111111",
|
|
||||||
args{
|
|
||||||
[]byte{0xFF},
|
|
||||||
},
|
|
||||||
[]bool{true, true, true, true, true, true, true, true},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"case-10101010",
|
|
||||||
args{
|
|
||||||
[]byte{0xAA},
|
|
||||||
},
|
|
||||||
[]bool{true, false, true, false, true, false, true, false},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := decodeBitfield(tt.args.payload); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("decodeBitfield() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,6 +21,8 @@ type peerState struct {
|
|||||||
peerChoking bool
|
peerChoking bool
|
||||||
// peer is interested in this client
|
// peer is interested in this client
|
||||||
peerInterested bool
|
peerInterested bool
|
||||||
|
|
||||||
|
bitfield peer.MsgBitfield
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用MSE加密来避免运营商对bt流量的封锁,基本上现在市面上BT客户端都默认开启了,不用MSE的话很多Peer拒绝连接
|
// 使用MSE加密来避免运营商对bt流量的封锁,基本上现在市面上BT客户端都默认开启了,不用MSE的话很多Peer拒绝连接
|
||||||
@@ -85,14 +87,14 @@ func (ps *peerState) download() error {
|
|||||||
buf := scanner.Bytes()
|
buf := scanner.Bytes()
|
||||||
message := &peer.Message{}
|
message := &peer.Message{}
|
||||||
message.Decode(buf)
|
message.Decode(buf)
|
||||||
switch peer.MessageType(message.Type) {
|
switch message.ID {
|
||||||
case peer.Keepalive:
|
case peer.Keepalive:
|
||||||
break
|
break
|
||||||
case peer.Choke:
|
case peer.Choke:
|
||||||
ps.peerChoking = true
|
ps.peerChoking = true
|
||||||
break
|
break
|
||||||
case peer.Unchoke:
|
case peer.Unchoke:
|
||||||
ps.peerChoking = false
|
ps.handleUnchoke()
|
||||||
break
|
break
|
||||||
case peer.Interested:
|
case peer.Interested:
|
||||||
ps.peerInterested = true
|
ps.peerInterested = true
|
||||||
@@ -103,9 +105,7 @@ func (ps *peerState) download() error {
|
|||||||
case peer.Have:
|
case peer.Have:
|
||||||
break
|
break
|
||||||
case peer.Bitfield:
|
case peer.Bitfield:
|
||||||
bitfield := peer.MsgBitfield(message.Payload)
|
ps.handleBitfield(message.Payload)
|
||||||
// 判断peer能提供需要下载的文件分片
|
|
||||||
|
|
||||||
break
|
break
|
||||||
case peer.Request:
|
case peer.Request:
|
||||||
break
|
break
|
||||||
@@ -117,3 +117,49 @@ func (ps *peerState) download() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *peerState) handleUnchoke() {
|
||||||
|
ps.peerChoking = false
|
||||||
|
// 如果客户端对peer感兴趣并且peer没有choked客户端,就可以开始下载了
|
||||||
|
if ps.amInterested {
|
||||||
|
have := ps.getHavePieces(ps.bitfield)
|
||||||
|
if len(have) > 0 {
|
||||||
|
/*for _, index := range have {
|
||||||
|
ps.conn.Write((&peer.Message{13, peer.Request,}))
|
||||||
|
}*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *peerState) handleBitfield(payload []byte) {
|
||||||
|
ps.bitfield = 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.amInterested = true
|
||||||
|
_, err = ps.conn.Write(peer.NewMessage(peer.Unchoke, []byte{}).Encode())
|
||||||
|
if err != nil {
|
||||||
|
ps.conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ps.amChoking = false
|
||||||
|
} else {
|
||||||
|
ps.conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取peer能提供需要下载的文件分片
|
||||||
|
func (ps *peerState) getHavePieces(bitfield peer.MsgBitfield) []int {
|
||||||
|
states := make([]bool, len(ps.torrent.PieceState))
|
||||||
|
for i := range states {
|
||||||
|
states[i] = ps.torrent.PieceState[i].complete
|
||||||
|
}
|
||||||
|
return bitfield.Have(states)
|
||||||
|
}
|
||||||
@@ -9,17 +9,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_peerState_handshake(t *testing.T) {
|
func Test_peerState_handshake(t *testing.T) {
|
||||||
torrent := buildTorrent()
|
|
||||||
tracker := &tracker.Tracker{
|
ps := getUsablePeer()
|
||||||
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()
|
handshake, err := ps.handshake()
|
||||||
@@ -31,6 +22,20 @@ func Test_peerState_handshake(t *testing.T) {
|
|||||||
fmt.Println(handshake)
|
fmt.Println(handshake)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_peerState_download(t *testing.T) {
|
||||||
|
|
||||||
|
ps := getUsablePeer()
|
||||||
|
|
||||||
|
// 发出握手请求
|
||||||
|
_, err := ps.handshake()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// 开始下载
|
||||||
|
err = ps.download()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func buildTorrent() *Torrent {
|
func buildTorrent() *Torrent {
|
||||||
metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
|
metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -41,7 +46,18 @@ func buildTorrent() *Torrent {
|
|||||||
return NewTorrent(peer.GenPeerID(), metaInfo)
|
return NewTorrent(peer.GenPeerID(), metaInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUsablePeer(torrent *Torrent, peers []peer.Peer) *peerState {
|
// 获取一个能连接上的peer
|
||||||
|
func getUsablePeer() *peerState {
|
||||||
|
torrent := buildTorrent()
|
||||||
|
tracker := &tracker.Tracker{
|
||||||
|
PeerID: torrent.PeerID,
|
||||||
|
MetaInfo: torrent.MetaInfo,
|
||||||
|
}
|
||||||
|
peers, err := tracker.Tracker()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Tracker end")
|
||||||
ch := make(chan *peerState, 1)
|
ch := make(chan *peerState, 1)
|
||||||
for i := range peers {
|
for i := range peers {
|
||||||
go peerTest(torrent, &peers[i], ch)
|
go peerTest(torrent, &peers[i], ch)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package torrent
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gopeed/down/bt/metainfo"
|
"gopeed/down/bt/metainfo"
|
||||||
"gopeed/down/bt/peer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Torrent struct {
|
type Torrent struct {
|
||||||
@@ -21,7 +20,3 @@ func NewTorrent(peerID [20]byte, metaInfo *metainfo.MetaInfo) *Torrent {
|
|||||||
}
|
}
|
||||||
return torrent
|
return torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查一个peer能否提供需要下载的文件分片
|
|
||||||
func CheckPeer(bitfield peer.MsgBitfield) {
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user