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); }
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)); } } }
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); }