Example #1
0
/**
  * 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;
}
Example #2
0
/**
  * @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;
}
Example #3
0
/**
  * @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;
}
Example #4
0
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");
}
Example #5
0
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;
}
Example #6
0
/**
  * 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;
}
Example #7
0
/**
  * @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;
}
Example #8
0
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);
}