Class: QuartzTorrent::PeerManager
- Inherits:
-
Object
- Object
- QuartzTorrent::PeerManager
- Defined in:
- lib/quartz_torrent/peermanager.rb
Overview
This class is used internally by PeerClient (The bittorrent protocol object) to choke, unchoke, and connect to peers for a specific torrent.
Instance Method Summary collapse
-
#initialize ⇒ PeerManager
constructor
A new instance of PeerManager.
-
#manageConnections(classifiedPeers) ⇒ Object
Determine if we need to connect to more peers.
-
#managePeers(classifiedPeers) ⇒ Object
Given a list of Peer objects (torrent peers), calculate the actions to take.
Constructor Details
#initialize ⇒ PeerManager
Returns a new instance of PeerManager.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/quartz_torrent/peermanager.rb', line 29 def initialize @logger = LogManager.getLogger("peer_manager") @targetActivePeerCount = 50 @targetUnchokedPeerCount = 4 @cachedHandshakingAndEstablishedCount = 0 # An array of Peers that we are allowing to download. @downloaders = [] @optimisticUnchokePeer = nil # A peer is considered newly connected when the number of seconds it has had it's connection established # is below this number. @newlyConnectedDuration = 60 @optimisticPeerChangeDuration = 30 @lastOptimisticPeerChangeTime = nil end |
Instance Method Details
#manageConnections(classifiedPeers) ⇒ Object
Determine if we need to connect to more peers. Returns a list of peers to connect to.
46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/quartz_torrent/peermanager.rb', line 46 def manageConnections(classifiedPeers) n = classifiedPeers.handshakingPeers.size + classifiedPeers.establishedPeers.size if n < @targetActivePeerCount result = classifiedPeers.disconnectedPeers.shuffle.first(@targetActivePeerCount - n) @logger.debug "There are #{n} peers connected or in handshaking. Will establish #{result.size} more connections to peers." result else [] end end |
#managePeers(classifiedPeers) ⇒ Object
Given a list of Peer objects (torrent peers), calculate the actions to take.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/quartz_torrent/peermanager.rb', line 60 def managePeers(classifiedPeers) result = ManagePeersResult.new @logger.debug "Manage peers: #{classifiedPeers.disconnectedPeers.size} disconnected, #{classifiedPeers.handshakingPeers.size} handshaking, #{classifiedPeers.establishedPeers.size} established" @logger.debug "Manage peers: #{classifiedPeers}" # Unchoke some peers. According to the specification: # # "...unchoking the four peers which have the best upload rate and are interested. These four peers are referred to as downloaders, because they are interested in downloading from the client." # "Peers which have a better upload rate (as compared to the downloaders) but aren't interested get unchoked. If they become interested, the downloader with the worst upload rate gets choked. # If a client has a complete file, it uses its upload rate rather than its download rate to decide which peers to unchoke." # "at any one time there is a single peer which is unchoked regardless of its upload rate (if interested, it counts as one of the four allowed downloaders). Which peer is optimistically # unchoked rotates every 30 seconds. Newly connected peers are three times as likely to start as the current optimistic unchoke as anywhere else in the rotation. This gives them a decent chance # of getting a complete piece to upload." # # This doesn't define initial rampup; On rampup we have no peer upload rate information. # We want to end up with: # - At most 4 peers that are both interested and unchoked. These are Downloaders. They should be the ones with # the best upload rate. # - All peers that have a better upload rate than Downloaders and are not interested are unchoked. # - One random peer that is unchoked. If it is interested, it is one of the 4 downloaders. # When choosing this random peer, peers that have connected in the last N seconds should be 3 times more # likely to be chosen. This peer only changes every 30 seconds. # Step 1: Pick the optimistic unchoke peer selectOptimisticPeer(classifiedPeers) # Step 2: Update the downloaders to be the interested peers with the best upload rate. if classifiedPeers.interestedPeers.size > 0 bestUploadInterested = classifiedPeers.interestedPeers.sort{ |a,b| b.uploadRate.value <=> a.uploadRate.value}.first(@targetUnchokedPeerCount) # If the optimistic unchoke peer is interested, he counts as a downloader. if @optimisticUnchokePeer && @optimisticUnchokePeer.peerInterested peerAlreadyIsDownloader = false bestUploadInterested.each do |peer| if peer.eql?(@optimisticUnchokePeer) peerAlreadyIsDownloader = true break end end bestUploadInterested[bestUploadInterested.size-1] = @optimisticUnchokePeer if ! peerAlreadyIsDownloader end # If one of the downloaders has changed, choke the peer downloadersMap = {} @downloaders.each{ |d| downloadersMap[d.trackerPeer] = d } bestUploadInterested.each do |peer| if downloadersMap.delete peer.trackerPeer # This peer was already a downloader. No changes. else # This peer wasn't a downloader before. Now it is; unchoke it result.unchoke.push peer if peer.peerChoked end end # Any peers remaining in the map are no longer a downloader. Choke them. result.choke = result.choke.concat(downloadersMap.values) @downloaders = bestUploadInterested end # Step 3: Unchoke all peers that have a better upload rate but are not interested. # However, if we just started up, only unchoke targetUnchokedPeerCount peers. if @downloaders.size > 0 if classifiedPeers.uninterestedPeers.size > 0 classifiedPeers.uninterestedPeers.each do |peer| if peer.uploadRate.value > @downloaders[0].uploadRate.value && peer.peerChoked result.unchoke.push peer end if peer.uploadRate.value < @downloaders[0].uploadRate.value && ! peer.peerChoked && ! peer.eql?(@optimisticUnchokePeer) result.choke.push peer end end end else # No downloaders yet, so we can't tell who is fast or not. Unchoke some result.unchoke = result.unchoke.concat(classifiedPeers.uninterestedPeers.first(@targetUnchokedPeerCount)) end @logger.debug "Manage peers result: #{result}" result end |