def connGotBitfield(self, connId): #got a bitfield from the peer, need to check if the peer already has some of the offered pieces self.lock.acquire() connSet = self.connInfo[connId] connStatus = connSet['conn'].getStatus() #check for offered pieces which the peer already has for pieceIndex in connStatus.getMatchingGotPieces(connSet['upPieces']): connSet['upPieces'].remove(pieceIndex) self.pieceStatus.decreaseAssignedUploads(pieceIndex) #offer new pieces if needed if len(connSet['upPieces']) < 2: wantedPieces = connStatus.getMatchingMissingPieces(self.ownStatus.getGotPieces()) wantedPieces.difference_update(connSet['upPieces']) pieces = self.pieceStatus.getUpPieces(wantedPieces, 2 - len(connSet['upPieces'])) if len(pieces) == 0: #no piece to offer self.log.debug('Conn %-6i - Nothing to offer (adding to waiting list)', connId) self.waitingConns.add(connId) else: #offer pieces self.log.debug('Conn %-6i - Offering pieces %s', connId, ', '.join(str(x) for x in pieces)) for pieceIndex in pieces: connSet['upPieces'].add(pieceIndex) connSet['conn'].send(Messages.generateHave(pieceIndex)) self.lock.release()
def gotNewPiece(self, pieceIndex): #we got a new piece - shouldn't happen during super seeding but whatever self.lock.acquire() for connId in self.waitingConns.copy(): connSet = self.connInfo[connId] if not connSet['conn'].getStatus().hasPiece(pieceIndex): self.log.debug('Conn %-6i - Offering piece %i', connId, pieceIndex) connSet['upPieces'].add(pieceIndex) self.pieceStatus.increaseAssignedUploads(pieceIndex) connSet['conn'].send(Messages.generateHave(pieceIndex)) if len(connSet['upPieces']) == 2: self.waitingConns.remove(connId) self.lock.release()
def addConn(self, connId, conn): #called when a new connection is established (before we get a bitfield from the peer) self.lock.acquire() upPieces = self.pieceStatus.getUpPieces(self.ownStatus.getGotPieces(), 2) assert len(upPieces) <= 2, 'Got more then we want: '+str(len(upPieces)) self.connInfo[connId] = {'conn':conn, 'upPieces':upPieces} if len(upPieces) == 2: #enough pieces to offer self.log.debug('Conn %-6i - Added, offering pieces %s', connId, ', '.join(str(x) for x in upPieces)) else: #not enough pieces to offer self.log.debug('Conn %-6i - Added, offering pieces %s (adding to waiting list)', connId, ', '.join(str(x) for x in upPieces)) self.waitingConns.add(connId) for pieceIndex in upPieces: conn.send(Messages.generateHave(pieceIndex)) self.lock.release()
def connGotPiece(self, connId, pieceIndex): #peer got a new piece, need to offer a new one if it was one of the pieces we offered self.lock.acquire() connSet = self.connInfo[connId] connStatus = connSet['conn'].getStatus() if connStatus.isSeed(): #does not need any pieces if pieceIndex in connSet['upPieces']: connSet['upPieces'].remove(pieceIndex) self.pieceStatus.decreaseAssignedUploads(pieceIndex) assert len(connSet['upPieces']) == 0, 'Does not need any pieces but we offer some?! (len: '+str(len(connSet['upPieces']))+')' self.log.debug('Conn %-6i - Got piece %i and does not need any other pieces (is now seed)', connId, pieceIndex) self.waitingConns.remove(connId) else: #needs some, so offer one more if the one obtained from it was one of the offered ones if not pieceIndex in connSet['upPieces']: #was not one of the offered pieces, nothing to do self.log.debug('Conn %-6i - Got piece %i which is not one of the offered pieces', connId, pieceIndex) else: #was one of the offered pieces, offer another one connSet['upPieces'].remove(pieceIndex) self.pieceStatus.decreaseAssignedUploads(pieceIndex) wantedPieces = connStatus.getMatchingMissingPieces(self.ownStatus.getGotPieces()) wantedPieces.difference_update(connSet['upPieces']) upPiece = self.pieceStatus.getUpPieces(wantedPieces, 1) assert len(upPiece) <= 1, 'Got more then we want: '+str(len(upPiece)) if len(upPiece) == 0: #no piece to offer self.log.debug('Conn %-6i - Got piece %i but nothing to offer (adding to waiting list)', connId, pieceIndex) self.waitingConns.add(connId) else: #offer piece upPiece = upPiece.pop() self.log.debug('Conn %-6i - Got piece %i, offering piece %i', connId, pieceIndex, upPiece) connSet['upPieces'].add(upPiece) connSet['conn'].send(Messages.generateHave(upPiece)) self.lock.release()
def _handleMessage(self, connId, conn, message): if message[0] == -1: #keepalive self.log.debug('Conn %i: Got keepalive', connId) elif message[0] == 0: #remote choke self.log.debug('Conn %i: Got choked', connId) self._getTorrentInfo(conn)['requester'].connGotChoked(conn) conn.setRemoteChoke(True) elif message[0] == 1: #remote unchoke self.log.debug('Conn %i: Got unchoked', connId) conn.setRemoteChoke(False) if conn.localInterested(): #and we are interested - means there should be something requestable self._getTorrentInfo(conn)['requester'].connGotUnchoked(conn) elif message[0] == 2: #remote interested self.log.debug('Conn %i: Peer is interested in us', connId) conn.setRemoteInterest(True) elif message[0] == 3: #remote not interested self.log.debug('Conn %i: Peer is no longer interested in us', connId) conn.setRemoteInterest(False) elif message[0] == 4: #remote got a new piece self.log.debug('Conn %i: Peer finished piece %i',\ connId, message[1]) torrent = self._getTorrentInfo(conn) status = conn.getStatus() status.gotPiece(message[1]) if torrent['ownStatus'].isFinished() and not status.hasMatchingMissingPieces(torrent['ownStatus'].getGotPieces()): #nothing to gain, nothing to give - diconnect self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False) else: #inform superseeding handler if needed if torrent['superSeedingEnabled']: torrent['superSeedingHandler'].connGotPiece(connId, message[1]) #check if we are again interested in that peer if (not conn.localInterested()) and torrent['ownStatus'].needsPiece(message[1]): #we were not interested, but now this peer got a piece we need, so we are interested self.log.debug('Conn %i: Interested in peer after he got piece %i', connId, message[1]) conn.setLocalInterest(True) if (not conn.remoteChoked()): #we are already unchoked, spawn some requests! self.log.debug('Conn %i: Peer unchoked us in the past!', connId) torrent['requester'].connGotUnchoked(conn) elif message[0] == 5: #remotes bitfield self.log.debug('Conn %i: Got bitfield', connId) torrent = self._getTorrentInfo(conn) status = conn.getStatus() status.addBitfield(message[1]) if torrent['ownStatus'].isFinished() and not status.hasMatchingMissingPieces(torrent['ownStatus'].getGotPieces()): #nothing to gain, nothing to give - diconnect self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False) else: #inform superseeding handler if needed if torrent['superSeedingEnabled']: torrent['superSeedingHandler'].connGotBitfield(connId) #check if the peer has something interesting if status.hasMatchingGotPieces(torrent['ownStatus'].getNeededPieces()): #yep he has self.log.debug('Conn %i: Interested in peer after getting bitfield', connId) conn.setLocalInterest(True) elif message[0] == 6: #remote request self.log.debug('Conn %i: Got request for piece %i with offset %i and length %i',\ connId, message[1][0], message[1][1], message[1][2]) dataHandle = self._getTorrentInfo(conn)['storage'].getDataHandle(message[1][0],message[1][1],message[1][2]) conn.addOutRequest(message[1][0],message[1][1],message[1][2], dataHandle) elif message[0] == 7: #got data self.log.debug('Conn %i: Got data for piece %i with offset %i and length %i',\ connId, message[1][0], message[1][1], len(message[1][2])) #remove request from list conn.finishedInRequest(message[1][0], message[1][1], len(message[1][2])) #notify requester torrent = self._getTorrentInfo(conn) if torrent['requester'].finishedRequest(message[1][2], conn, message[1][0], message[1][1]): #finished to retrieve a whole piece self.log.debug('Piece %i is finished', message[1][0]) #deal with peers gotPieces = torrent['ownStatus'].getGotPieces() neededPieces = torrent['ownStatus'].getNeededPieces() weAreFinished = torrent['ownStatus'].isFinished() weAreSuperSeeding = torrent['superSeedingEnabled'] for connId in torrent['connIds'].copy(): conn = self.conns[connId] status = conn.getStatus() #send have if needed if not weAreSuperSeeding: conn.send(Messages.generateHave(message[1][0])) if weAreFinished and not status.hasMatchingMissingPieces(gotPieces): #nothing to gain, nothing to give - diconnect self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False) else: if conn.localInterested(): #we were interested up to now if not status.hasMatchingGotPieces(neededPieces): #nothing to request anymore conn.setLocalInterest(False) torrent['requester'].connGotNotInteresting(conn) if weAreSuperSeeding: torrent['superSeedingHandler'].gotNewPiece(message[1][0]) elif message[0] == 8: #cancel self.log.debug('Conn %i: Peer canceled request of piece %i with offset %i and length %i', connId, message[1][0], message[1][1], message[1][2]) conn.delOutRequest(message[1][0], message[1][1], message[1][2]) else: self.log.error('Conn %i: Unmatched message with ID: %d - shouldn\'t reach this point!', connId, message[0])