Qt 5.11 | Qt Network | Torrent Example | peerwireclient.h Example File

Qt 5.11.3 Reference Documentation

# peerwireclient.h Example File

torrent/peerwireclient.h

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      #ifndef PEERWIRECLIENT_H
      #define PEERWIRECLIENT_H

      #include <QBitArray>
      #include <QList>
      #include <QTcpSocket>

      class QHostAddress;
      class QTimerEvent;

      class TorrentPeer;

      struct TorrentBlock
      {
          inline TorrentBlock(int p, int o, int l)
              : pieceIndex(p), offset(o), length(l)
          {
          }
          inline bool operator==(const TorrentBlock &other) const
          {
              return pieceIndex == other.pieceIndex
                      && offset == other.offset
                      && length == other.length;
          }

          int pieceIndex;
          int offset;
          int length;
      };

      class PeerWireClient : public QTcpSocket
      {
          Q_OBJECT

      public:
          enum PeerWireStateFlag {
              ChokingPeer = 0x1,
              InterestedInPeer = 0x2,
              ChokedByPeer = 0x4,
              PeerIsInterested = 0x8
          };
          Q_DECLARE_FLAGS(PeerWireState, PeerWireStateFlag)

          explicit PeerWireClient(const QByteArray &peerId, QObject *parent = 0);
          void initialize(const QByteArray &infoHash, int pieceCount);

          void setPeer(TorrentPeer *peer);
          TorrentPeer *peer() const;

          // State
          inline PeerWireState peerWireState() const { return pwState; }
          QBitArray availablePieces() const;
          QList<TorrentBlock> incomingBlocks() const;

          // Protocol
          void chokePeer();
          void unchokePeer();
          void sendInterested();
          void sendKeepAlive();
          void sendNotInterested();
          void sendPieceNotification(int piece);
          void sendPieceList(const QBitArray &bitField);
          void requestBlock(int piece, int offset, int length);
          void cancelRequest(int piece, int offset, int length);
          void sendBlock(int piece, int offset, const QByteArray &data);

          // Rate control
          qint64 writeToSocket(qint64 bytes);
          qint64 readFromSocket(qint64 bytes);
          qint64 downloadSpeed() const;
          qint64 uploadSpeed() const;

          bool canTransferMore() const;
          qint64 bytesAvailable() const override { return incomingBuffer.size() + QTcpSocket::bytesAvailable(); }
          qint64 socketBytesAvailable() const { return socket.bytesAvailable(); }
          qint64 socketBytesToWrite() const { return socket.bytesToWrite(); }

          void setReadBufferSize(qint64 size) override;

          void connectToHost(const QHostAddress &address,
                             quint16 port, OpenMode openMode = ReadWrite) override;
          void diconnectFromHost();

      signals:
          void infoHashReceived(const QByteArray &infoHash);
          void readyToTransfer();

          void choked();
          void unchoked();
          void interested();
          void notInterested();

          void piecesAvailable(const QBitArray &pieces);
          void blockRequested(int pieceIndex, int begin, int length);
          void blockReceived(int pieceIndex, int begin, const QByteArray &data);

          void bytesReceived(qint64 size);

      protected:
          void timerEvent(QTimerEvent *event) override;

          qint64 readData(char *data, qint64 maxlen) override;
          qint64 readLineData(char *data, qint64 maxlen) override;
          qint64 writeData(const char *data, qint64 len) override;

      private slots:
          void sendHandShake();
          void processIncomingData();
          void socketStateChanged(QAbstractSocket::SocketState state);

      private:
          // Data waiting to be read/written
          QByteArray incomingBuffer;
          QByteArray outgoingBuffer;

          struct BlockInfo {
              int pieceIndex;
              int offset;
              int length;
              QByteArray block;
          };
          QList<BlockInfo> pendingBlocks;
          int pendingBlockSizes;
          QList<TorrentBlock> incoming;

          enum PacketType {
              ChokePacket = 0,
              UnchokePacket = 1,
              InterestedPacket = 2,
              NotInterestedPacket = 3,
              HavePacket = 4,
              BitFieldPacket = 5,
              RequestPacket = 6,
              PiecePacket = 7,
              CancelPacket = 8
          };

          // State
          PeerWireState pwState;
          bool receivedHandShake;
          bool gotPeerId;
          bool sentHandShake;
          int nextPacketLength;

          // Upload/download speed records
          qint64 uploadSpeedData[8];
          qint64 downloadSpeedData[8];
          int transferSpeedTimer;

          // Timeout handling
          int timeoutTimer;
          int pendingRequestTimer;
          bool invalidateTimeout;
          int keepAliveTimer;

          // Checksum, peer ID and set of available pieces
          QByteArray infoHash;
          QByteArray peerIdString;
          QBitArray peerPieces;
          TorrentPeer *torrentPeer;

          QTcpSocket socket;
      };

      Q_DECLARE_OPERATORS_FOR_FLAGS(PeerWireClient::PeerWireState)

      #endif