class Torrent(): def __init__(self, torrent_file, target_file, port = PEER_PORT, error_handler = None, tracker_retry_time = consts['TRACKER_RETRY_TIME']): self.running = False self.is_downloading = False self.completed = False self.paused = False self.peer_port = port self.error_handler = error_handler self.tracker_retry_time = tracker_retry_time self.data = read_torrent_file(torrent_file) self.info_hash = sha1(encode(self.data["info"])).digest() self.peer_id = generate_peer_id() self.handshake = generate_handshake(self.info_hash, self.peer_id) self.tracker_thread = None # check is seed self.target_file = target_file self.storage = Storage(self.data['info']) self.piece_num = self.storage.piece_num print 'piece_num:', self.piece_num import os if os.path.exists(target_file): self.storage.set_file(target_file) self.picker = PiecePicker(self) self.selector = PeerSelector(self) self.downloading = [] self.count_received = {} def __del__(self): """ Stop the tracker thread. """ if self.tracker_thread != None: self.tracker_loop.join() def handleError(self, err): print 'Error: ' + err + '\n\t' + traceback.format_exc() if self.error_handler != None: self.error_handler(err) def run(self): """ Start the torrent running. """ try: if not self.running: self.running = True # run in main thread #self.tracker_loop = Thread(target = self.perform_tracker_request, \ # args = (self.data["announce"], self.info_hash, self.peer_id)) #self.tracker_loop.start() self._perform_tracker_request(self.data["announce"], self.info_hash, self.peer_id) self._perform_mainLoop() self._cleanup(self.data["announce"], self.info_hash, self.peer_id) except Exception as e: self.handleError(repr(e)) def stop(self): """ Stop the torrent from running. """ if self.running: self.running = False if self.is_downloading: self.is_downloading = False reactor.stop() self._cleanup(self.data["announce"], self.info_hash, self.peer_id) if self.tracker_thread != None: self.tracker_loop.join() def pause(self, paused = True): if self.paused == paused: return self.paused = paused if paused: # choke all for connection in self.torrent.connections.values(): connection.choke() ################################ # Interface ################################ def newConnection(self, connection): self.connections[connection.peer_id] = connection print '[Torrent]\ttotal connected peers: ', len(self.connections) connection.bitfield(self.storage.gen_complete_str()) def lostConnection(self, connection): if connection.peer_id in self.connections: del self.connections[connection.peer_id] self._cleanup_connection(connection) print '[Torrent]\ttotal connected peers: ', len(self.connections) ################################ # Event ################################ def onRequest(self, connection, block_info): if self.paused: return data = self.storage.get(*block_info) connection.piece(block_info, data) def onPiece(self, connection, block_info, block_data): if self.paused: return self.storage.push(block_info[0], block_info[1], block_data) if self.storage.is_piece_received(block_info[0]): self.pushHave(block_info[0]) if self.storage.is_all_piece_received(): # save target file self.storage.save_target_file(self.target_file) self.onComplete() request = (connection, block_info) self.downloading.remove(request) self._try_download() print 'push piece', block_info, len(block_data), ', rate:', repr(self.getDownloadRate() / 1000) + 'kb/s, complete:', repr(self.storage.get_downloaded_rate()*100)+'%' if connection.peer_id not in self.count_received.keys(): self.count_received[connection.peer_id] = 1 else: self.count_received[connection.peer_id] += 1 def onCancel(self, connection, block_info): # now nothing to do pass def onUnchoked(self, connection): self._try_download() def onComplete(self): if self.completed: return time_used = time() - self.start_time if time_used == 0: time_used = 1e-7 print '[Torrent]\tDownload Completed!', 'Total time:', time_used, 'Speed:', repr(self.storage.length / time_used / 1024) + 'kb/s' for peer_id in self.count_received: print '\t\tFrom #' + peer_id + ' received:', self.count_received[peer_id] self.completed = True # inform the tracker self.tracker_thread = Thread(target = self._inform_tracker_completed, \ args = (self.data["announce"], self.info_hash, self.peer_id)) self.tracker_thread.start() def pushHave(self, piece_index): for connection in self.connections.values(): connection.have(piece_index) ################################ # Util ################################ def checkBlockInfo(self, block_info): if block_info[0] not in range(0, self.piece_num): return False return True def getUsableConnections(self): ret = [] in_use = [request[0].peer_id for request in self.downloading] for connection in self.connections.values(): if (not connection.is_choked) and (connection.peer_id not in in_use): ret.append(connection) return ret def getUnchokedConnections(self): return [conn for conn in self.connections.values() if not conn.is_choked] def getDownloadRate(self): rate = 0 for conn in self.connections.values(): rate += conn.getDownloadRate() return rate def getUploadRate(self): rate = 0 for conn in self.connections.values(): rate += conn.getUploadRate() return rate def hasPiece(self, piece_index): return self.storage.is_piece_received(piece_index) def isSeed(self): return self.completed ################################ # Privates ################################ def _perform_mainLoop(self): """ Run torrent main logic """ self.is_downloading = True self.connections = {} for peer_info in self.peers: reactor.connectTCP(peer_info[0], peer_info[1], BTPeerClientFactory(self)) self._launch_timer() reactor.listenTCP(self.peer_port, BTPeerServerFactory(self)) self.start_time = time() if self.storage.is_all_piece_received(): self.onComplete() reactor.run() def _perform_tracker_request(self, url, info_hash, peer_id): """ Make a tracker request to url, every interval seconds, using the info_hash and peer_id, and decode the peers on a good response. """ cnt = 0 while self.running and cnt < self.tracker_retry_time: self.tracker_response = make_tracker_request(info_hash, peer_id, url, peer_port = self.peer_port) if "failure reason" not in self.tracker_response: self.peers = get_peers(self.tracker_response["peers"]) print '[Torrent]\tpeers:', self.peers return sleep(self.tracker_response["interval"]) raise Exception('can not connect to tracker!') def _inform_tracker_completed(self, url, info_hash, peer_id): try: self.tracker_response = make_tracker_request(info_hash, peer_id, url, event = 'completed', peer_port = self.peer_port) if "failure reason" not in self.tracker_response: print '[Torrent]\tinform tracker completed.' except Exception, e: pass
from storage import Storage info = {} info["piece_length"] = 524288 info["length"] = 524288 * 8 st = Storage(info) print("is all piece received: ", st.is_all_piece_received()) for i in range(8): for j in range(32): st.push(i, j*16*1024, "sdfsdfsdf") print("is all piece received: ", st.gen_priority_list()) print("is all piece received: ", st.is_all_piece_received())