/** * Send an UDP unicast datagram to the given peer. * @return 'false' if the datagram can't be sent. */ INetworkListener::SendStatus UDPListener::send(Common::MessageHeader::MessageType type, const google::protobuf::Message& message, const Common::Hash& peerID) { PM::IPeer* peer = this->peerManager->getPeer(peerID); if (!peer) return INetworkListener::PEER_UNKNOWN; int messageSize; if (!(messageSize = this->writeMessageToBuffer(type, message))) return INetworkListener::MESSAGE_TOO_LARGE; L_DEBU(QString("Send unicast UDP to %1, header.getType(): %2, message size: %3 \n%4"). arg(peer->toStringLog()). arg(Common::MessageHeader::messToStr(type)). arg(messageSize). arg(Common::ProtoHelper::getDebugStr(message)) ); if (this->unicastSocket.writeDatagram(this->buffer, messageSize, peer->getIP(), peer->getPort()) == -1) { L_WARN(QString("Unable to send datagram (unicast): error: %1").arg(this->unicastSocket.errorString())); return INetworkListener::UNABLE_TO_SEND; } return INetworkListener::OK; }
/** * @return A null header if error. */ Common::MessageHeader UDPListener::readDatagramToBuffer(QUdpSocket& socket, QHostAddress& peerAddress) { quint16 port; const qint64 datagramSize = socket.readDatagram(this->buffer, BUFFER_SIZE, &peerAddress, &port); if (datagramSize == -1) { L_WARN(QString("UDPListener::readDatagramToBuffer(..): Unable to read multicast datagram from address:port: %1:%2").arg(peerAddress.toString()).arg(port)); return Common::MessageHeader(); } Common::MessageHeader header = Common::MessageHeader::readHeader(buffer); if (header.getSize() > datagramSize - Common::MessageHeader::HEADER_SIZE) { L_ERRO("The message size (header.size) exceeds the datagram size received"); header.setNull(); return header; } if (header.getSenderID() == this->peerManager->getSelf()->getID()) { // L_WARN("We receive a datagram from ourself, skip"); // Don't care . . . header.setNull(); return header; } if (header.getType() != Common::MessageHeader::CORE_IM_ALIVE) { PM::IPeer* peer = this->peerManager->getPeer(header.getSenderID()); if (!peer) { L_WARN(QString("We receive a datagram from an unknown peer (%1), skip").arg(peerAddress.toString())); header.setNull(); return header; } if (!peer->isAlive()) { L_WARN(QString("We receive a datagram from a dead peer (%1), skip").arg(peerAddress.toString())); header.setNull(); return header; } L_DEBU(QString("Receive a datagram UDP from %1, %2").arg(peer->toStringLog()).arg(header.toStr())); } else { L_DEBU(QString("Receive a datagram UDP from %1, %2").arg(header.getSenderID().toStr()).arg(header.toStr())); } return header; }
/** * @return A null header if error. */ Common::MessageHeader UDPListener::readDatagramToBuffer(QUdpSocket& socket, QHostAddress& peerAddress) { qint64 datagramSize = socket.readDatagram(this->buffer, BUFFER_SIZE, &peerAddress); Common::MessageHeader header = Common::MessageHeader::readHeader(buffer); if (header.getSize() > datagramSize - Common::MessageHeader::HEADER_SIZE) { L_ERRO("header.getSize() > datagramSize"); header.setNull(); return header; } if (header.getSenderID() == this->peerManager->getID()) { // L_WARN("We receive a datagram from ourself, skip"); // Don't care.. header.setNull(); return header; } if (header.getType() != Common::MessageHeader::CORE_IM_ALIVE) { PM::IPeer* peer = this->peerManager->getPeer(header.getSenderID()); if (!peer) { L_WARN("We receive a datagram from an unknown peer, skip"); header.setNull(); return header; } if (!peer->isAlive()) { L_WARN("We receive a datagram from a dead peer, skip"); header.setNull(); return header; } L_DEBU(QString("Receive a datagram UDP from %1, %2").arg(peer->toStringLog()).arg(header.toStr())); } else { L_DEBU(QString("Receive a datagram UDP from %1, %2").arg(header.getSenderID().toStr()).arg(header.toStr())); } return header; }
void UDPListener::send(Common::MessageHeader::MessageType type, const Common::Hash& peerID, const google::protobuf::Message& message) { PM::IPeer* peer = this->peerManager->getPeer(peerID); if (!peer) { L_WARN(QString("Unable to find the peer %1").arg(peerID.toStr())); return; } int messageSize; if (!(messageSize = this->writeMessageToBuffer(type, message))) return; L_DEBU(QString("Send unicast UDP to %1 : header.getType() = %2, message size = %3 \n%4"). arg(peer->toStringLog()). arg(Common::MessageHeader::messToStr(type)). arg(messageSize). arg(Common::ProtoHelper::getDebugStr(message)) ); if (this->unicastSocket.writeDatagram(this->buffer, messageSize, peer->getIP(), peer->getPort()) == -1) L_WARN("Unable to send datagram"); }
int ChunkDownloader::getNumberOfFreePeer() { QMutexLocker locker(&this->mutex); int n = 0; bool isTheNmberOfPeersHasChanged = false; for (QMutableListIterator<PM::IPeer*> i(this->peers); i.hasNext();) { PM::IPeer* peer = i.next(); if (!peer->isAvailable()) { i.remove(); this->linkedPeers.rmLink(peer); isTheNmberOfPeersHasChanged = true; } else if (this->occupiedPeersDownloadingChunk.isPeerFree(peer)) n++; } if (isTheNmberOfPeersHasChanged) emit numberOfPeersChanged(); return n; }
/** * Get the fastest free peer, may remove dead peers. */ PM::IPeer* ChunkDownloader::getTheFastestFreePeer() { QMutexLocker locker(&this->mutex); PM::IPeer* current = nullptr; bool isTheNmberOfPeersHasChanged = false; for (QMutableListIterator<PM::IPeer*> i(this->peers); i.hasNext();) { PM::IPeer* peer = i.next(); if (!peer->isAvailable()) { i.remove(); this->linkedPeers.rmLink(peer); isTheNmberOfPeersHasChanged = true; } else if (this->occupiedPeersDownloadingChunk.isPeerFree(peer) && (!current || peer->getSpeed() > current->getSpeed())) current = peer; } if (isTheNmberOfPeersHasChanged) emit numberOfPeersChanged(); return current; }
/** * @remarks This method may remove dead peers from the list. */ QList<PM::IPeer*> ChunkDownloader::getPeers() { QMutexLocker locker(&this->mutex); QList<PM::IPeer*> peers; peers.reserve(this->peers.size()); bool isTheNmberOfPeersHasChanged = false; for (QMutableListIterator<PM::IPeer*> i(this->peers); i.hasNext();) { PM::IPeer* peer = i.next(); if (peer->isAvailable()) peers << peer; else { i.remove(); this->linkedPeers.rmLink(peer); isTheNmberOfPeersHasChanged = true; } } if (isTheNmberOfPeersHasChanged) emit numberOfPeersChanged(); return peers; }
void ChunkDownloader::run() { int deltaRead = 0; QElapsedTimer timer; timer.start(); this->lastTransferStatus = QUEUED; try { QSharedPointer<FM::IDataWriter> writer = this->chunk->getDataWriter(); static const int SOCKET_TIMEOUT = SETTINGS.get<quint32>("socket_timeout"); static const int TIME_PERIOD_CHOOSE_ANOTHER_PEER = 1000.0 * SETTINGS.get<double>("time_recheck_chunk_factor") * SETTINGS.get<quint32>("chunk_size") / SETTINGS.get<quint32>("lan_speed"); static const int BUFFER_SIZE = SETTINGS.get<quint32>("buffer_size_writing"); char buffer[BUFFER_SIZE]; const int initialKnownBytes = this->chunk->getKnownBytes(); int bytesToRead = this->chunkSize - initialKnownBytes; int bytesToWrite = 0; int bytesWritten = 0; forever { this->mutex.lock(); if (!this->downloading) { L_DEBU(QString("Downloading aborted, chunk : %1%2").arg(this->chunk->toStringLog()).arg(this->chunk->isComplete() ? "" : " Not complete!")); this->closeTheSocket = true; // Because some garbage from the remote uploader will continue to come in this socket. this->mutex.unlock(); break; } this->mutex.unlock(); int bytesRead = this->socket->read(buffer + bytesToWrite, bytesToRead < BUFFER_SIZE - bytesToWrite ? bytesToRead : BUFFER_SIZE - bytesToWrite); bytesToRead -= bytesRead; if (bytesRead == 0) { if (!this->socket->waitForReadyRead(SOCKET_TIMEOUT)) { L_WARN(QString("Connection dropped, error = %1, bytesAvailable = %2").arg(socket->errorString()).arg(socket->bytesAvailable())); this->closeTheSocket = true; this->lastTransferStatus = TRANSFER_ERROR; break; } continue; } else if (bytesRead == -1) { L_WARN(QString("Socket : cannot receive data : %1").arg(this->chunk->toStringLog())); this->closeTheSocket = true; this->lastTransferStatus = TRANSFER_ERROR; break; } deltaRead += bytesRead; bytesToWrite += bytesRead; if (timer.elapsed() > TIME_PERIOD_CHOOSE_ANOTHER_PEER) { this->currentDownloadingPeer->setSpeed(deltaRead / timer.elapsed() * 1000); L_DEBU(QString("Check for a better peer for the chunk: %1, current peer: %2 . . .").arg(this->chunk->toStringLog()).arg(this->currentDownloadingPeer->toStringLog())); timer.start(); deltaRead = 0; // If a another peer exists and its speed is greater than our by a factor 'switch_to_another_peer_factor' // then we will try to switch to this peer. PM::IPeer* peer = this->getTheFastestFreePeer(); if ( peer && peer != this->currentDownloadingPeer && peer->getSpeed() / SETTINGS.get<double>("switch_to_another_peer_factor") > this->currentDownloadingPeer->getSpeed() ) { L_DEBU(QString("Switch to a better peer: %1").arg(peer->toStringLog())); this->closeTheSocket = true; // We ask to close the socket to avoid to get garbage data. break; } } // If the buffer is full or there is no more byte to read. if (bytesToWrite == BUFFER_SIZE || bytesToRead == 0) { writer->write(buffer, bytesToWrite); bytesWritten += bytesToWrite; bytesToWrite = 0; } this->transferRateCalculator.addData(bytesRead); if (initialKnownBytes + bytesWritten >= this->chunkSize) break; } } catch(FM::FileResetException) { L_DEBU("FileResetException"); this->closeTheSocket = true; this->lastTransferStatus = FILE_NON_EXISTENT; } catch(FM::ChunkDataUnknownException) { L_DEBU("ChunkDataUnknownException"); this->closeTheSocket = true; this->lastTransferStatus = UNABLE_TO_OPEN_THE_FILE; } catch(FM::UnableToOpenFileInWriteModeException) { L_DEBU("UnableToOpenFileInWriteModeException"); this->closeTheSocket = true; this->lastTransferStatus = UNABLE_TO_OPEN_THE_FILE; } catch(FM::IOErrorException&) { L_DEBU("IOErrorException"); this->closeTheSocket = true; this->lastTransferStatus = FILE_IO_ERROR; } catch (FM::ChunkDeletedException&) { L_DEBU("ChunkDeletedException"); this->closeTheSocket = true; this->lastTransferStatus = FILE_NON_EXISTENT; } catch (FM::TryToWriteBeyondTheEndOfChunkException&) { L_DEBU("TryToWriteBeyondTheEndOfChunkException"); this->closeTheSocket = true; this->lastTransferStatus = GOT_TOO_MUCH_DATA; } catch (FM::hashMissmatchException) { static const quint32 BLOCK_DURATION = SETTINGS.get<quint32>("block_duration_corrupted_data"); L_USER(QString(tr("Corrupted data received for the file \"%1\" from peer %2. Peer blocked for %3 ms")).arg(this->chunk->getFilePath()).arg(this->currentDownloadingPeer->getNick()).arg(BLOCK_DURATION)); /*: A reason why the user has been blocked */ this->currentDownloadingPeer->block(BLOCK_DURATION, tr("Has sent corrupted data")); this->closeTheSocket = true; this->lastTransferStatus = HASH_MISSMATCH; } if (timer.elapsed() > MINIMUM_DELTA_TIME_TO_COMPUTE_SPEED) this->currentDownloadingPeer->setSpeed(deltaRead / timer.elapsed() * 1000); this->socket->setReadBufferSize(0); this->socket->moveToThread(this->mainThread); }