Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/webtorrent/webtorrent.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/torrent.js142
1 files changed, 78 insertions, 64 deletions
diff --git a/lib/torrent.js b/lib/torrent.js
index 689d5ef..caa6687 100644
--- a/lib/torrent.js
+++ b/lib/torrent.js
@@ -480,7 +480,7 @@ class Torrent extends EventEmitter {
// Select only specified files (BEP53) http://www.bittorrent.org/beps/bep_0053.html
if (this.so) {
- const selectOnlyFiles = parseRange.parse(this.so)
+ const selectOnlyFiles = parseRange(this.so)
this.files.forEach((v, i) => {
if (selectOnlyFiles.includes(i)) this.files[i].select(true)
@@ -1055,11 +1055,13 @@ class Torrent extends EventEmitter {
wire.on('bitfield', () => {
updateSeedStatus()
this._update()
+ this._updateWireInterest(wire)
})
wire.on('have', () => {
updateSeedStatus()
this._update()
+ this._updateWireInterest(wire)
})
wire.once('interested', () => {
@@ -1091,7 +1093,9 @@ class Torrent extends EventEmitter {
})
wire.bitfield(this.bitfield) // always send bitfield (required)
- wire.uninterested() // always start out uninterested (as per protocol)
+
+ // initialize interest in case bitfield message was already received before above handler was registered
+ this._updateWireInterest(wire)
// Send PORT message to peers that support DHT
if (wire.peerExtensions.dht && this.client.dht && this.client.dht.listening) {
@@ -1154,24 +1158,26 @@ class Torrent extends EventEmitter {
const prev = this._amInterested
this._amInterested = !!this._selections.length
- this.wires.forEach(wire => {
- let interested = false
- for (let index = 0; index < this.pieces.length; ++index) {
- if (this.pieces[index] && wire.peerPieces.get(index)) {
- interested = true
- break
- }
- }
-
- if (interested) wire.interested()
- else wire.uninterested()
- })
+ this.wires.forEach(wire => this._updateWireInterest(wire))
if (prev === this._amInterested) return
if (this._amInterested) this.emit('interested')
else this.emit('uninterested')
}
+ _updateWireInterest (wire) {
+ let interested = false
+ for (let index = 0; index < this.pieces.length; ++index) {
+ if (this.pieces[index] && wire.peerPieces.get(index)) {
+ interested = true
+ break
+ }
+ }
+
+ if (interested) wire.interested()
+ else wire.uninterested()
+ }
+
/**
* Heartbeat to update all peers and their requests.
*/
@@ -1349,71 +1355,79 @@ class Torrent extends EventEmitter {
_rechoke () {
if (!this.ready) return
- if (this._rechokeOptimisticTime > 0) this._rechokeOptimisticTime -= 1
- else this._rechokeOptimisticWire = null
+ // wires in increasing order of quality (pop() gives next best peer)
+ const wireStack =
+ this.wires
+ .map(wire => ({ wire, random: Math.random() })) // insert a random seed for randomizing the sort
+ .sort((objA, objB) => {
+ const wireA = objA.wire
+ const wireB = objB.wire
+
+ // prefer peers that send us data faster
+ if (wireA.downloadSpeed() !== wireB.downloadSpeed()) {
+ return wireA.downloadSpeed() - wireB.downloadSpeed()
+ }
- const peers = []
+ // then prefer peers that can download data from us faster
+ if (wireA.uploadSpeed() !== wireB.uploadSpeed()) {
+ return wireA.uploadSpeed() - wireB.uploadSpeed()
+ }
- this.wires.forEach(wire => {
- if (!wire.isSeeder && wire !== this._rechokeOptimisticWire) {
- peers.push({
- wire,
- downloadSpeed: wire.downloadSpeed(),
- uploadSpeed: wire.uploadSpeed(),
- salt: Math.random(),
- isChoked: true
- })
- }
- })
+ // then prefer already unchoked peers (to minimize fibrillation)
+ if (wireA.amChoking !== wireB.amChoking) {
+ return wireA.amChoking ? -1 : 1 // choking < unchoked
+ }
- peers.sort(rechokeSort)
+ // otherwise random order
+ return objA.random - objB.random
+ })
+ .map(obj => obj.wire) // return array of wires (remove random seed)
- let unchokeInterested = 0
- let i = 0
- for (; i < peers.length && unchokeInterested < this._rechokeNumSlots; ++i) {
- peers[i].isChoked = false
- if (peers[i].wire.peerInterested) unchokeInterested += 1
+ if (this._rechokeOptimisticTime <= 0) {
+ // clear old optimistic peer, so it can be rechoked normally and then replaced
+ this._rechokeOptimisticWire = null
+ } else {
+ this._rechokeOptimisticTime -= 1
}
- // Optimistically unchoke a peer
- if (!this._rechokeOptimisticWire && i < peers.length && this._rechokeNumSlots) {
- const candidates = peers.slice(i).filter(peer => peer.wire.peerInterested)
- const optimistic = candidates[randomInt(candidates.length)]
+ let numInterestedUnchoked = 0
+ // leave one rechoke slot open for optimistic unchoking
+ while (wireStack.length > 0 && numInterestedUnchoked < this._rechokeNumSlots - 1) {
+ const wire = wireStack.pop() // next best quality peer
- if (optimistic) {
- optimistic.isChoked = false
- this._rechokeOptimisticWire = optimistic.wire
- this._rechokeOptimisticTime = RECHOKE_OPTIMISTIC_DURATION
+ if (wire.isSeeder || wire === this._rechokeOptimisticWire) {
+ continue
}
- }
- // Unchoke best peers
- peers.forEach(peer => {
- if (peer.wire.amChoking !== peer.isChoked) {
- if (peer.isChoked) peer.wire.choke()
- else peer.wire.unchoke()
- }
- })
+ wire.unchoke()
- function rechokeSort (peerA, peerB) {
- // Prefer higher download speed
- if (peerA.downloadSpeed !== peerB.downloadSpeed) {
- return peerB.downloadSpeed - peerA.downloadSpeed
+ // only stop unchoking once we fill the slots with interested peers that will actually download
+ if (wire.peerInterested) {
+ numInterestedUnchoked++
}
+ }
- // Prefer higher upload speed
- if (peerA.uploadSpeed !== peerB.uploadSpeed) {
- return peerB.uploadSpeed - peerA.uploadSpeed
- }
+ // fill optimistic unchoke slot if empty
+ if (this._rechokeOptimisticWire === null && this._rechokeNumSlots > 0) {
+ // don't optimistically unchoke uninterested peers
+ const remaining = wireStack.filter(wire => wire.peerInterested)
- // Prefer unchoked
- if (peerA.wire.amChoking !== peerB.wire.amChoking) {
- return peerA.wire.amChoking ? 1 : -1
- }
+ if (remaining.length > 0) {
+ // select random remaining (not yet unchoked) peer
+ const newOptimisticPeer = remaining[randomInt(remaining.length)]
+
+ newOptimisticPeer.unchoke()
- // Random order
- return peerA.salt - peerB.salt
+ this._rechokeOptimisticWire = newOptimisticPeer
+
+ this._rechokeOptimisticTime = RECHOKE_OPTIMISTIC_DURATION
+ }
}
+
+ // choke the rest
+ wireStack
+ .filter(wire => wire !== this._rechokeOptimisticWire) // except the optimistically unchoked peer
+ .forEach(wire => wire.choke())
}
/**