Example #1
0
 /**
  * Manages handshaking with the client.
  *
  * If seeders_stop_seeding is set to a number greater than 0,
  * we check if we have at least N seeders beyond ourselves for the requested
  * torrent and if so, stop seeding (to spare bandwidth).
  *
  * @param Client $client Client
  *
  * @throws Error\CloseConnection In case when the request is invalid
  *                               or we don't want or cannot serve
  *                               the requested torrent.
  * @return void
  */
 protected function shakeHand(Client $client)
 {
     $protocol_length = unpack('C', $client->socketRead(1));
     $protocol_length = current($protocol_length);
     $protocol = $client->socketRead($protocol_length);
     if ($protocol !== self::PROTOCOL_STRING) {
         $this->logger->logError("Client tries to connect with unsupported protocol: " . substr($protocol, 0, 100) . ". Closing connection.");
         throw new Error\CloseConnection('Unsupported protocol.');
     }
     // 8 reserved void bytes.
     $client->socketRead(8);
     $info_hash = $client->socketRead(20);
     $client->setPeerId($client->socketRead(20));
     $info_hash_readable = unpack('H*', $info_hash);
     $info_hash_readable = reset($info_hash_readable);
     $torrent = $this->persistence->getTorrent($info_hash);
     if (!isset($torrent)) {
         throw new Error\CloseConnection('Unknown info hash.');
     }
     $client->setTorrent($torrent);
     // If we have X other seeders already, we stop seeding on our own.
     if (0 < ($seeders_stop_seeding = $this->seeders_stop_seeding)) {
         $stats = $this->persistence->getPeerStats($info_hash, $this->peer_id);
         if ($stats['complete'] >= $seeders_stop_seeding) {
             $this->logger->logMessage("External seeder limit ({$seeders_stop_seeding}) reached " . "for info hash {$info_hash_readable}, stopping seeding.");
             throw new Error\CloseConnection('Stop seeding, we have others to seed.');
         }
     }
     // Our handshake signal.
     $client->socketWrite(pack('C', strlen(self::PROTOCOL_STRING)) . self::PROTOCOL_STRING . pack('a8', '') . $info_hash . pack('a20', $this->peer_id));
     $this->logger->logMessage("Handshake completed with peer {$client->getPeerId()} " . "with address {$client->getAddress()}:{$client->getPort()}, " . "info hash: {$info_hash_readable}.");
 }
 /**
  * Save announce for all the torrents in the database
  * so clients know where to connect.
  *
  * This method runs in infinite loop repeating
  * announcing every self::ANNOUNCE_INTERVAL seconds.
  *
  * @return void
  */
 protected function announce()
 {
     $persistence = $this->persistence;
     $iterations = 0;
     do {
         $all_torrents = $persistence->getAllInfoHash();
         foreach ($all_torrents as $torrent_info) {
             $persistence->saveAnnounce($torrent_info['info_hash'], $this->peer->getPeerId(), $this->peer->getExternalAddress(), $this->peer->getPort(), $torrent_info['length'], 0, 0, 'complete', self::ANNOUNCE_INTERVAL);
         }
         $this->logger->logMessage('Seeder server announced itself for ' . count($all_torrents) . " torrents at address {$this->peer->getExternalAddress()}:" . "{$this->peer->getPort()} (announces every " . self::ANNOUNCE_INTERVAL . 's).');
         sleep(self::ANNOUNCE_INTERVAL);
         // Memory leak prevention, see self::STOP_AFTER_ITERATIONS.
     } while (++$iterations < self::STOP_AFTER_ITERATIONS);
     $this->logger->logMessage('Announce process restarts to prevent memory leaks.');
     exit(0);
 }