예제 #1
0
void UDPListener::sendIMAliveMessage()
{
   Protos::Core::IMAlive IMAliveMessage;
   IMAliveMessage.set_version(PROTOCOL_VERSION);
   ProtoHelper::setStr(IMAliveMessage, &Protos::Core::IMAlive::set_core_version, Common::Global::getVersionFull());
   IMAliveMessage.set_port(this->UNICAST_PORT);
   Common::ProtoHelper::setStr(IMAliveMessage, &Protos::Core::IMAlive::set_nick, this->peerManager->getNick());

   IMAliveMessage.set_amount(this->fileManager->getAmount());
   IMAliveMessage.set_download_rate(this->downloadManager->getDownloadRate());
   IMAliveMessage.set_upload_rate(this->uploadManager->getUploadRate());

   this->currentIMAliveTag = this->mtrand.randInt();
   this->currentIMAliveTag <<= 32;
   this->currentIMAliveTag |= this->mtrand.randInt();
   IMAliveMessage.set_tag(this->currentIMAliveTag);

   static const quint32 NUMBER_OF_HASHES_TO_SEND = SETTINGS.get<quint32>("number_of_hashes_sent_imalive");
   this->currentChunkDownloads = this->downloadManager->getUnfinishedChunks(NUMBER_OF_HASHES_TO_SEND);
   IMAliveMessage.mutable_chunk()->Reserve(this->currentChunkDownloads.size());
   for (QListIterator< QSharedPointer<DM::IChunkDownload> > i(this->currentChunkDownloads); i.hasNext();)
   {
      IMAliveMessage.add_chunk()->set_hash(i.next()->getHash().getData(), Common::Hash::HASH_SIZE);
   }

   this->send(Common::MessageHeader::CORE_IM_ALIVE, IMAliveMessage);
}
예제 #2
0
void UDPListener::processPendingMulticastDatagrams()
{
   while (this->multicastSocket.hasPendingDatagrams())
   {
      QHostAddress peerAddress;
      const Common::MessageHeader& header =  UDPListener::readDatagramToBuffer(this->multicastSocket, peerAddress);
      if (header.isNull())
         continue;

      switch (header.getType())
      {
      case Common::MessageHeader::CORE_IM_ALIVE:
         {
            Protos::Core::IMAlive IMAliveMessage;
            const bool readOk = IMAliveMessage.ParseFromArray(this->bodyBuffer, header.getSize());

            if (!readOk)
            {
               L_WARN(QString("Unable to read the IMAlive message from peer %1 %2").arg(header.getSenderID().toStr()).arg(peerAddress.toString()));
               break;
            }
            else if (IMAliveMessage.version() != PROTOCOL_VERSION) // If the protocol version doesn't match we don't add the peer.
            {
               L_WARN(
                  QString("The peer %1 %2 %3 doesn't have the same protocol version (%4) as us (%5). It will be ignored.")
                     .arg(Common::ProtoHelper::getStr(IMAliveMessage, &Protos::Core::IMAlive::nick))
                     .arg(header.getSenderID().toStr())
                     .arg(peerAddress.toString())
                     .arg(IMAliveMessage.version())
                     .arg(PROTOCOL_VERSION)
               );
               break;
            }

            this->peerManager->updatePeer(
               header.getSenderID(),
               peerAddress,
               IMAliveMessage.port(),
               Common::ProtoHelper::getStr(IMAliveMessage, &Protos::Core::IMAlive::nick),
               IMAliveMessage.amount(),
               Common::ProtoHelper::getStr(IMAliveMessage, &Protos::Core::IMAlive::core_version)
            );

            if (IMAliveMessage.chunk_size() > 0)
            {
               QList<Common::Hash> hashes;
               hashes.reserve(IMAliveMessage.chunk_size());
               for (int i = 0; i < IMAliveMessage.chunk_size(); i++)
                  hashes << IMAliveMessage.chunk(i).hash();

               QBitArray bitArray = this->fileManager->haveChunks(hashes);

               if (!bitArray.isNull()) // If we own at least one chunk we reply with a CHUNKS_OWNED message.
               {
                  Protos::Core::ChunksOwned chunkOwnedMessage;
                  chunkOwnedMessage.set_tag(IMAliveMessage.tag());
                  chunkOwnedMessage.mutable_chunk_state()->Reserve(bitArray.size());
                  for (int i = 0; i < bitArray.size(); i++)
                     chunkOwnedMessage.add_chunk_state(bitArray[i]);
                  this->send(Common::MessageHeader::CORE_CHUNKS_OWNED, header.getSenderID(), chunkOwnedMessage);
               }
            }
         }
         break;

      case Common::MessageHeader::CORE_CHAT_MESSAGE:
         {
            Protos::Core::ChatMessage chatMessage;
            chatMessage.ParseFromArray(this->bodyBuffer, header.getSize());
            emit newChatMessage(header.getSenderID(), chatMessage);
         }
         break;

      case Common::MessageHeader::CORE_FIND: // Find.
         {
            Protos::Core::Find findMessage;
            findMessage.ParseFromArray(this->bodyBuffer, header.getSize());

            QList<Protos::Common::FindResult> results =
               this->fileManager->find(
                  Common::ProtoHelper::getStr(findMessage, &Protos::Core::Find::pattern),
                  SETTINGS.get<quint32>("max_number_of_search_result_to_send"),
                  SETTINGS.get<quint32>("max_udp_datagram_size") - Common::MessageHeader::HEADER_SIZE
               );

            for (QMutableListIterator<Protos::Common::FindResult> i(results); i.hasNext();)
            {
               Protos::Common::FindResult& result = i.next();
               result.set_tag(findMessage.tag());
               this->send(Common::MessageHeader::CORE_FIND_RESULT, header.getSenderID(), result);
            }
         }
         break;

      default:
         L_WARN(QString("Unkown header type from multicast socket : %1").arg(header.getType(), 0, 16));
      }
   }
}
예제 #3
0
void UDPListener::sendIMAliveMessage()
{
   Protos::Core::IMAlive IMAliveMessage;
   IMAliveMessage.set_version(PROTOCOL_VERSION);
   Common::ProtoHelper::setStr(IMAliveMessage, &Protos::Core::IMAlive::set_core_version, Common::Global::getVersionFull());
   IMAliveMessage.set_port(this->UNICAST_PORT);

   const QString& nick = this->peerManager->getSelf()->getNick();
   Common::ProtoHelper::setStr(IMAliveMessage, &Protos::Core::IMAlive::set_nick, nick.length() > MAX_NICK_LENGTH ? nick.left(MAX_NICK_LENGTH) : nick);

   IMAliveMessage.set_amount(this->fileManager->getAmount());
   IMAliveMessage.set_download_rate(this->downloadManager->getDownloadRate());
   IMAliveMessage.set_upload_rate(this->uploadManager->getUploadRate());

   this->currentIMAliveTag = this->mtrand.randInt();
   this->currentIMAliveTag <<= 32;
   this->currentIMAliveTag |= this->mtrand.randInt();
   IMAliveMessage.set_tag(this->currentIMAliveTag);

   // We fill the rest of the message with a maximum of needed hashes.
   static const quint32 MAX_IMALIVE_THROUGHPUT = SETTINGS.get<quint32>("max_imalive_throughput");
   static const int AVERAGE_FIXED_SIZE = 100; // [Byte]. Header size + information in the 'IMAlive' message without the hashes.
   static const quint32 IMALIVE_PERIOD = SETTINGS.get<quint32>("peer_imalive_period") / 1000; // [s]
   static const int FIXED_RATE_PER_PEER = AVERAGE_FIXED_SIZE / IMALIVE_PERIOD; // [Byte/s]
   static const int HASH_SIZE = Common::Hash::HASH_SIZE + 4; // "4" is the overhead added by protobuff for each hash.

   const int numberOfPeers = this->peerManager->getNbOfPeers();
   const int maxNumberOfHashesToSend = numberOfPeers == 0 ? std::numeric_limits<int>::max() : IMALIVE_PERIOD * (MAX_IMALIVE_THROUGHPUT - numberOfPeers * FIXED_RATE_PER_PEER) / (numberOfPeers * HASH_SIZE);

   int numberOfHashesToSend = (this->MAX_UDP_DATAGRAM_PAYLOAD_SIZE - IMAliveMessage.ByteSize() - Common::MessageHeader::HEADER_SIZE) / HASH_SIZE;
   if (numberOfHashesToSend > maxNumberOfHashesToSend)
      numberOfHashesToSend = maxNumberOfHashesToSend;

   // The requested hashes method alternates from the first hashes and the oldest hashes.
   // We are trying to have the knowledge about who has which chunk for the whole download queue (IDownloadManager::getTheOldestUnfinishedChunks(..))
   // and for the chunks we want to download first (IDownloadManager::getTheFirstUnfinishedChunks(..)).
   switch (this->nextHashRequestType)
   {
   case FIRST_HASHES:
      this->currentChunkDownloaders = this->downloadManager->getTheFirstUnfinishedChunks(numberOfHashesToSend);
      this->nextHashRequestType = OLDEST_HASHES;
      break;
   case OLDEST_HASHES:
      this->currentChunkDownloaders = this->downloadManager->getTheOldestUnfinishedChunks(numberOfHashesToSend);
      this->nextHashRequestType = FIRST_HASHES;
      break;
   }

   IMAliveMessage.mutable_chunk()->Reserve(this->currentChunkDownloaders.size());
   for (QListIterator<QSharedPointer<DM::IChunkDownloader>> i(this->currentChunkDownloaders); i.hasNext();)
   {
      QSharedPointer<DM::IChunkDownloader> chunkDownloader = i.next();
      IMAliveMessage.add_chunk()->set_hash(chunkDownloader->getHash().getData(), Common::Hash::HASH_SIZE);

      // If we already have the chunk . . .
      QSharedPointer<FM::IChunk> chunk = this->fileManager->getChunk(chunkDownloader->getHash());
      if (!chunk.isNull() && chunk->isComplete())
         chunkDownloader->addPeer(this->peerManager->getSelf());
      else
         chunkDownloader->rmPeer(this->peerManager->getSelf());
   }

   emit IMAliveMessageToBeSend(IMAliveMessage);

   this->send(Common::MessageHeader::CORE_IM_ALIVE, IMAliveMessage);
}