/** * 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}."); }
/** * Called on child processes after forking. * * For slot 0: Starts seeding peer. * For slot 1: Starts announcing loop. * * @param integer $slot The slot (numbered index) of the fork. * Reused when recreating process. * * @throws Error * @return void */ public function startChildProcess($slot) { if ($this->persistence instanceof Persistence\ResetWhenForking) { $this->persistence->resetAfterForking(); } switch ($slot) { case 0: $this->peer->start(); break; case 1: $this->announce(); break; default: throw new Error('Invalid process slot while running seeder server.'); } }