Prepare to allow max conns per torrent to be configured
This commit is contained in:
parent
12191dbfa3
commit
326b36545b
72
client.go
72
client.go
@ -264,7 +264,7 @@ func NewClient(cfg *Config) (cl *Client, err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
cl = &Client{
|
cl = &Client{
|
||||||
halfOpenLimit: socketsPerTorrent,
|
halfOpenLimit: defaultHalfOpenConnsPerTorrent,
|
||||||
config: *cfg,
|
config: *cfg,
|
||||||
defaultStorage: cfg.DefaultStorage,
|
defaultStorage: cfg.DefaultStorage,
|
||||||
dopplegangerAddrs: make(map[string]struct{}),
|
dopplegangerAddrs: make(map[string]struct{}),
|
||||||
@ -382,7 +382,7 @@ func (cl *Client) waitAccept() {
|
|||||||
defer cl.mu.Unlock()
|
defer cl.mu.Unlock()
|
||||||
for {
|
for {
|
||||||
for _, t := range cl.torrents {
|
for _, t := range cl.torrents {
|
||||||
if cl.wantConns(t) {
|
if t.wantConns() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -936,7 +936,7 @@ func (cl *Client) runHandshookConn(c *connection, t *Torrent) {
|
|||||||
c.rw,
|
c.rw,
|
||||||
}
|
}
|
||||||
completedHandshakeConnectionFlags.Add(c.connectionFlags(), 1)
|
completedHandshakeConnectionFlags.Add(c.connectionFlags(), 1)
|
||||||
if !cl.addConnection(t, c) {
|
if !t.addConnection(c) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer t.dropConnection(c)
|
defer t.dropConnection(c)
|
||||||
@ -1337,67 +1337,10 @@ func (cl *Client) connectionLoop(t *Torrent, c *connection) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the connection is added.
|
|
||||||
func (cl *Client) addConnection(t *Torrent, c *connection) bool {
|
|
||||||
if cl.closed.IsSet() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !cl.wantConns(t) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, c0 := range t.conns {
|
|
||||||
if c.PeerID == c0.PeerID {
|
|
||||||
// Already connected to a client with that ID.
|
|
||||||
duplicateClientConns.Add(1)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(t.conns) >= socketsPerTorrent {
|
|
||||||
c := t.worstBadConn(cl)
|
|
||||||
if c == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if cl.config.Debug && missinggo.CryHeard() {
|
|
||||||
log.Printf("%s: dropping connection to make room for new one:\n %s", t, c)
|
|
||||||
}
|
|
||||||
c.Close()
|
|
||||||
t.deleteConnection(c)
|
|
||||||
}
|
|
||||||
if len(t.conns) >= socketsPerTorrent {
|
|
||||||
panic(len(t.conns))
|
|
||||||
}
|
|
||||||
t.conns = append(t.conns, c)
|
|
||||||
c.t = t
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cl *Client) usefulConn(t *Torrent, c *connection) bool {
|
|
||||||
if c.closed.IsSet() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !t.haveInfo() {
|
|
||||||
return c.supportsExtension("ut_metadata")
|
|
||||||
}
|
|
||||||
if t.seeding() {
|
|
||||||
return c.PeerInterested
|
|
||||||
}
|
|
||||||
return t.connHasWantedPieces(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cl *Client) wantConns(t *Torrent) bool {
|
|
||||||
if !t.seeding() && !t.needData() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(t.conns) < socketsPerTorrent {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return t.worstBadConn(cl) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cl *Client) openNewConns(t *Torrent) {
|
func (cl *Client) openNewConns(t *Torrent) {
|
||||||
defer t.updateWantPeersEvent()
|
defer t.updateWantPeersEvent()
|
||||||
for len(t.peers) != 0 {
|
for len(t.peers) != 0 {
|
||||||
if !cl.wantConns(t) {
|
if !t.wantConns() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(t.halfOpen) >= cl.halfOpenLimit {
|
if len(t.halfOpen) >= cl.halfOpenLimit {
|
||||||
@ -1431,9 +1374,7 @@ func (cl *Client) badPeerIPPort(ip net.IP, port int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a Torrent without any attachment to a Client. That means we can
|
// Return a Torrent ready for insertion into a Client.
|
||||||
// initialize fields all fields that don't require the Client without locking
|
|
||||||
// it.
|
|
||||||
func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
|
func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
|
||||||
t = &Torrent{
|
t = &Torrent{
|
||||||
cl: cl,
|
cl: cl,
|
||||||
@ -1444,7 +1385,8 @@ func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
|
|||||||
halfOpen: make(map[string]struct{}),
|
halfOpen: make(map[string]struct{}),
|
||||||
pieceStateChanges: pubsub.NewPubSub(),
|
pieceStateChanges: pubsub.NewPubSub(),
|
||||||
|
|
||||||
storageOpener: cl.defaultStorage,
|
storageOpener: cl.defaultStorage,
|
||||||
|
maxEstablishedConns: defaultEstablishedConnsPerTorrent,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -653,3 +653,19 @@ func (cn *connection) wroteBytes(b []byte) {
|
|||||||
cn.stats.wroteBytes(b)
|
cn.stats.wroteBytes(b)
|
||||||
cn.t.stats.wroteBytes(b)
|
cn.t.stats.wroteBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether the connection is currently useful to us. We're seeding and
|
||||||
|
// they want data, we don't have metainfo and they can provide it, etc.
|
||||||
|
func (c *connection) useful() bool {
|
||||||
|
t := c.t
|
||||||
|
if c.closed.IsSet() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !t.haveInfo() {
|
||||||
|
return c.supportsExtension("ut_metadata")
|
||||||
|
}
|
||||||
|
if t.seeding() {
|
||||||
|
return c.PeerInterested
|
||||||
|
}
|
||||||
|
return t.connHasWantedPieces(c)
|
||||||
|
}
|
||||||
|
@ -36,9 +36,10 @@ const (
|
|||||||
// http://www.bittorrent.org/beps/bep_0005.html
|
// http://www.bittorrent.org/beps/bep_0005.html
|
||||||
defaultExtensionBytes = "\x00\x00\x00\x00\x00\x10\x00\x01"
|
defaultExtensionBytes = "\x00\x00\x00\x00\x00\x10\x00\x01"
|
||||||
|
|
||||||
socketsPerTorrent = 80
|
defaultEstablishedConnsPerTorrent = 80
|
||||||
torrentPeersHighWater = 200
|
defaultHalfOpenConnsPerTorrent = 80
|
||||||
torrentPeersLowWater = 50
|
torrentPeersHighWater = 200
|
||||||
|
torrentPeersLowWater = 50
|
||||||
|
|
||||||
// Limit how long handshake can take. This is to reduce the lingering
|
// Limit how long handshake can take. This is to reduce the lingering
|
||||||
// impact of a few bad apples. 4s loses 1% of successful handshakes that
|
// impact of a few bad apples. 4s loses 1% of successful handshakes that
|
||||||
|
67
torrent.go
67
torrent.go
@ -61,7 +61,8 @@ type Torrent struct {
|
|||||||
// The info dict. nil if we don't have it (yet).
|
// The info dict. nil if we don't have it (yet).
|
||||||
info *metainfo.InfoEx
|
info *metainfo.InfoEx
|
||||||
// Active peer connections, running message stream loops.
|
// Active peer connections, running message stream loops.
|
||||||
conns []*connection
|
conns []*connection
|
||||||
|
maxEstablishedConns int
|
||||||
// Set of addrs to which we're attempting to connect. Connections are
|
// Set of addrs to which we're attempting to connect. Connections are
|
||||||
// half-open until all handshakes are completed.
|
// half-open until all handshakes are completed.
|
||||||
halfOpen map[string]struct{}
|
halfOpen map[string]struct{}
|
||||||
@ -133,12 +134,8 @@ func (t *Torrent) addrActive(addr string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) worstConns(cl *Client) (wcs *worstConns) {
|
func (t *Torrent) worstConns() (wcs *worstConns) {
|
||||||
wcs = &worstConns{
|
wcs = &worstConns{make([]*connection, 0, len(t.conns))}
|
||||||
c: make([]*connection, 0, len(t.conns)),
|
|
||||||
t: t,
|
|
||||||
cl: cl,
|
|
||||||
}
|
|
||||||
for _, c := range t.conns {
|
for _, c := range t.conns {
|
||||||
if !c.closed.IsSet() {
|
if !c.closed.IsSet() {
|
||||||
wcs.c = append(wcs.c, c)
|
wcs.c = append(wcs.c, c)
|
||||||
@ -446,11 +443,7 @@ func (t *Torrent) writeStatus(w io.Writer, cl *Client) {
|
|||||||
fmt.Fprintf(w, "Pending peers: %d\n", len(t.peers))
|
fmt.Fprintf(w, "Pending peers: %d\n", len(t.peers))
|
||||||
fmt.Fprintf(w, "Half open: %d\n", len(t.halfOpen))
|
fmt.Fprintf(w, "Half open: %d\n", len(t.halfOpen))
|
||||||
fmt.Fprintf(w, "Active peers: %d\n", len(t.conns))
|
fmt.Fprintf(w, "Active peers: %d\n", len(t.conns))
|
||||||
sort.Sort(&worstConns{
|
sort.Sort(&worstConns{t.conns})
|
||||||
c: t.conns,
|
|
||||||
t: t,
|
|
||||||
cl: cl,
|
|
||||||
})
|
|
||||||
for i, c := range t.conns {
|
for i, c := range t.conns {
|
||||||
fmt.Fprintf(w, "%2d. ", i+1)
|
fmt.Fprintf(w, "%2d. ", i+1)
|
||||||
c.WriteStatus(w, t)
|
c.WriteStatus(w, t)
|
||||||
@ -740,15 +733,15 @@ func (t *Torrent) extentPieces(off, _len int64) (pieces []int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) worstBadConn(cl *Client) *connection {
|
func (t *Torrent) worstBadConn() *connection {
|
||||||
wcs := t.worstConns(cl)
|
wcs := t.worstConns()
|
||||||
heap.Init(wcs)
|
heap.Init(wcs)
|
||||||
for wcs.Len() != 0 {
|
for wcs.Len() != 0 {
|
||||||
c := heap.Pop(wcs).(*connection)
|
c := heap.Pop(wcs).(*connection)
|
||||||
if c.UnwantedChunksReceived >= 6 && c.UnwantedChunksReceived > c.UsefulChunksReceived {
|
if c.UnwantedChunksReceived >= 6 && c.UnwantedChunksReceived > c.UsefulChunksReceived {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
if wcs.Len() >= (socketsPerTorrent+1)/2 {
|
if wcs.Len() >= (t.maxEstablishedConns+1)/2 {
|
||||||
// Give connections 1 minute to prove themselves.
|
// Give connections 1 minute to prove themselves.
|
||||||
if time.Since(c.completedHandshake) > time.Minute {
|
if time.Since(c.completedHandshake) > time.Minute {
|
||||||
return c
|
return c
|
||||||
@ -1273,3 +1266,47 @@ func (t *Torrent) addPeers(peers []Peer) {
|
|||||||
func (t *Torrent) Stats() TorrentStats {
|
func (t *Torrent) Stats() TorrentStats {
|
||||||
return t.stats
|
return t.stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the connection is added.
|
||||||
|
func (t *Torrent) addConnection(c *connection) bool {
|
||||||
|
if t.cl.closed.IsSet() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !t.wantConns() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c0 := range t.conns {
|
||||||
|
if c.PeerID == c0.PeerID {
|
||||||
|
// Already connected to a client with that ID.
|
||||||
|
duplicateClientConns.Add(1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(t.conns) >= t.maxEstablishedConns {
|
||||||
|
c := t.worstBadConn()
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.cl.config.Debug && missinggo.CryHeard() {
|
||||||
|
log.Printf("%s: dropping connection to make room for new one:\n %s", t, c)
|
||||||
|
}
|
||||||
|
c.Close()
|
||||||
|
t.deleteConnection(c)
|
||||||
|
}
|
||||||
|
if len(t.conns) >= t.maxEstablishedConns {
|
||||||
|
panic(len(t.conns))
|
||||||
|
}
|
||||||
|
t.conns = append(t.conns, c)
|
||||||
|
c.t = t
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) wantConns() bool {
|
||||||
|
if !t.seeding() && !t.needData() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(t.conns) < t.maxEstablishedConns {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return t.worstBadConn() != nil
|
||||||
|
}
|
||||||
|
@ -4,11 +4,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implements heap functions such that [0] is the worst connection.
|
// Implements a heap of connections by how useful they are or have been.
|
||||||
type worstConns struct {
|
type worstConns struct {
|
||||||
c []*connection
|
c []*connection
|
||||||
t *Torrent
|
|
||||||
cl *Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wc *worstConns) Len() int { return len(wc.c) }
|
func (wc *worstConns) Len() int { return len(wc.c) }
|
||||||
@ -44,8 +42,8 @@ func (wc worstConnsSortKey) Less(other worstConnsSortKey) bool {
|
|||||||
|
|
||||||
func (wc *worstConns) key(i int) (key worstConnsSortKey) {
|
func (wc *worstConns) key(i int) (key worstConnsSortKey) {
|
||||||
c := wc.c[i]
|
c := wc.c[i]
|
||||||
key.useful = wc.cl.usefulConn(wc.t, c)
|
key.useful = c.useful()
|
||||||
if wc.t.seeding() {
|
if c.t.seeding() {
|
||||||
key.lastHelpful = c.lastChunkSent
|
key.lastHelpful = c.lastChunkSent
|
||||||
}
|
}
|
||||||
// Intentionally consider the last time a chunk was received when seeding,
|
// Intentionally consider the last time a chunk was received when seeding,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user