org.klomp.snark

## Class PeerCoordinator

- java.lang.Object
- - org.klomp.snark.PeerCoordinator

- All Implemented Interfaces:
    BandwidthListener, PeerListener

      class PeerCoordinator
      extends Object
      implements PeerListener, BandwidthListener

  Coordinates peer activities for a single torrent including upload/download management, peer selection, piece distribution, and bandwidth allocation.

  This class manages the core BitTorrent coordination logic:

  - Managing connected peers and their states
  - Choking/unchoking logic for upload slots
  - Piece selection and rarest-first distribution
  - Request queue management and pipeline control
  - Bandwidth distribution among peers
  - Peer Exchange (PEX) for peer discovery
  - DHT integration for trackerless operation
  - Tracker announcements and response processing

  Thread safety: This class uses extensive synchronization for thread-safe peer management. External access to some fields is documented where permitted.

  See Also:
    `Peer`, `PeerState`, `Storage`

- - ### Field Summary

    Fields
    Modifier and Type | Field and Description
    --- | ---
    `(package private) static long` | `CHECK_PERIOD`
    `static long` | `MAX_INACTIVE`
    `static long` | `MAX_SEED_INACTIVE`
    `(package private) static int` | `MAX_UPLOADERS`
    `(package private) MetaInfo` | `metainfo` External use by PeerMonitorTask only.
    `(package private) Deque<Peer>` | `peers` synchronize on this when changing peers or downloaders.
    `(package private) static int` | `RATE_DEPTH`
    `(package private) Storage` | `storage` External use by PeerMonitorTask only.

  - ### Constructor Summary

    Constructors
    Constructor and Description
    `PeerCoordinator(I2PSnarkUtil util, byte[] id, byte[] infohash, MetaInfo metainfo, Storage storage, CoordinatorListener listener, Snark torrent, BandwidthListener bwl)`

  - ### Method Summary

    All Methods Static Methods Instance Methods Concrete Methods
    Modifier and Type | Method and Description
    --- | ---
    `void` | `addInterestedAndChoking(int toAdd)`
    `boolean` | `addPeer(Peer peer)` Add peer (inbound or outbound)
    `int` | `allowedUploaders()` Return number of allowed uploaders for this torrent.
    `void` | `banWebPeer(String host, boolean isPermanent)` Ban a web peer for this torrent, for while or permanently.
    `boolean` | `completed()`
    `void` | `connected(Peer peer)` Called when the connection to the peer has started and the handshake was successfull.
    `void` | `decrementUploaders(boolean isInterested)` Decrement the uploaders and (if set) the interestedUploaders counts
    `void` | `disconnected(Peer peer)` Called when the connection to the peer was terminated or the connection handshake failed.
    `void` | `downloaded(int size)` Called when a peer has downloaded some bytes of a piece.
    `long` | `getCurrentUploadRate()` Returns the rate in Bps over last complete CHECK_PERIOD seconds
    `long` | `getDownBWLimit()` Current limit in Bps
    `long` | `getDownloaded()` Returns the total number of downloaded bytes of all peers.
    `long` | `getDownloadRate()` Returns the average rate in Bps over last RATE_DEPTH * CHECK_PERIOD seconds
    `byte[]` | `getID()`
    `byte[]` | `getInfoHash()`
    `int` | `getInterestedAndChoking()`
    `int` | `getInterestedUploaders()` Uploaders, interested only.
    `long` | `getLeft()` Bytes not yet in storage.
    `MetaInfo` | `getMetaInfo()`
    `String` | `getName()`
    `long` | `getNeededLength()` Bytes still wanted.
    `PartialPiece` | `getPartialPiece(Peer peer, BitField havePieces)` Return partial piece to the PeerState if it's still wanted and peer has it.
    `int` | `getPeerCount()` might be wrong
    `int` | `getPeers()` should be right
    `(package private) Set<PeerID>` | `getPEXPeers()` Called by TrackerClient
    `(package private) static long` | `getRate(long[] array)`
    `Storage` | `getStorage()`
    `long` | `getUpBWLimit()` Current limit in Bps
    `long` | `getUploaded()` Returns the total number of uploaded bytes of all peers.
    `int` | `getUploaders()` Uploaders whether interested or not Use this for per-torrent limits.
    `long` | `getUploadRate()` Returns the average rate in Bps over last RATE_DEPTH * CHECK_PERIOD seconds
    `I2PSnarkUtil` | `getUtil()` Convenience
    `boolean` | `gotBitField(Peer peer, BitField bitfield)` Returns true if the given bitfield contains at least one piece we are interested in.
    `void` | `gotChoke(Peer peer, boolean choke)` this does nothing but logging
    `void` | `gotCommentReq(Peer peer, int num)` Called when comments are requested via ut_comment
    `void` | `gotComments(Peer peer, List<Comment> comments)` Called when comments are received via ut_comment
    `void` | `gotExtension(Peer peer, int id, byte[] bs)` PeerListener callback
    `boolean` | `gotHave(Peer peer, int piece)` Called when a have piece message is received.
    `void` | `gotInterest(Peer peer, boolean interest)` Called when an interested message is received.
    `void` | `gotPeers(Peer peer, List<PeerID> peers)` Get peers from PEX - PeerListener callback
    `boolean` | `gotPiece(Peer peer, PartialPiece pp)` Returns false if the piece is no good (according to the hash).
    `void` | `gotPort(Peer peer, int port, int rport)` PeerListener callback Tell the DHT to ping it, this will get back the node info
    `ByteArray` | `gotRequest(Peer peer, int piece, int off, int len)` Returns a byte array containing the requested piece or null of the piece is unknown.
    `void` | `halt()`
    `boolean` | `halted()`
    `boolean` | `isWebPeerBanned(String host)` Is a web peer banned?
    `boolean` | `needOutboundPeers()` Outbound.
    `boolean` | `needPeers()` Inbound.
    `boolean` | `needPiece(Peer peer, BitField havePieces)` Called when we are downloading from the peer and may need to ask for a new piece.
    `boolean` | `overDownBWLimit()` Are we currently over the limit?
    `boolean` | `overUpBWLimit()` Is snark as a whole over its limit?
    `boolean` | `overUpBWLimit(long total)` Is a particular peer who has downloaded this many bytes from us in the last CHECK_PERIOD over its limit?
    `List<Peer>` | `peerList()` for web page detailed stats
    `void` | `restart()`
    `void` | `savePartialPieces(Peer peer, List<Request> partials)` Save partial pieces on peer disconnection and hopefully restart it later.
    `(package private) void` | `sendCommentReq(Peer peer)` Send a commment request message to the peer, if he supports it.
    `(package private) void` | `sendDHT(Peer peer)` Send a DHT message to the peer, if we both support DHT.
    `(package private) void` | `sendPeers(Peer peer)` Send a PEX message to the peer, if he supports PEX.
    `(package private) static void` | `setRate(long val, long[] array)`
    `void` | `setRateHistory(long up, long down)` Push the total uploaded/downloaded onto a RATE_DEPTH deep stack
    `void` | `setStorage(Storage stg)` Sets the storage after transition out of magnet mode Snark calls this after we call gotMetaInfo()
    `void` | `setUploaded(long up)` Sets the initial total of uploaded bytes of all peers (from a saved status)
    `void` | `setUploaders(int upl, int inter)` Set the uploaders and interestedUploaders counts
    `void` | `setWantedPieces()` Only called externally from Storage after the double-check fails.
    `boolean` | `shouldRequest(Peer peer, int size)` Should we request this many bytes?
    `boolean` | `shouldSend(int size)` Should we send this many bytes? Do NOT call uploaded() if this returns true.
    `(package private) void` | `unchokePeer()` (Optimistically) unchoke.
    `void` | `updatePiecePriorities()` Maps file priorities to piece priorities.
    `void` | `uploaded(int size)` Called when a peer has uploaded some bytes of a piece.

    - ### Methods inherited from class java.lang.Object

      `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Field Detail

    - #### CHECK_PERIOD

          static final long CHECK_PERIOD

      See Also:
        Constant Field Values

    - #### MAX_INACTIVE

          public static final long MAX_INACTIVE

      See Also:
        Constant Field Values

    - #### MAX_SEED_INACTIVE

          public static final long MAX_SEED_INACTIVE

      See Also:
        Constant Field Values

    - #### MAX_UPLOADERS

          static final int MAX_UPLOADERS

      See Also:
        Constant Field Values

    - #### metainfo

          MetaInfo metainfo

      External use by PeerMonitorTask only. Will be null when in magnet mode.

    - #### peers

          final Deque<Peer> peers

      synchronize on this when changing peers or downloaders. This is a Queue, not a Set, because PeerCheckerTask keeps things in order for choking/unchoking. External use by PeerMonitorTask only.

    - #### RATE_DEPTH

          static final int RATE_DEPTH

      See Also:
        Constant Field Values

    - #### storage

          Storage storage

      External use by PeerMonitorTask only. Will be null when in magnet mode.

  - ### Constructor Detail

    - #### PeerCoordinator

          public PeerCoordinator(I2PSnarkUtil util,
                                 byte[] id,
                                 byte[] infohash,
                                 MetaInfo metainfo,
                                 Storage storage,
                                 CoordinatorListener listener,
                                 Snark torrent,
                                 BandwidthListener bwl)

      Parameters:
        `metainfo` - null if in magnet mode
        `storage` - null if in magnet mode

  - ### Method Detail

    - #### addInterestedAndChoking

          public void addInterestedAndChoking(int toAdd)

    - #### addPeer

          public boolean addPeer(Peer peer)

      Add peer (inbound or outbound)
      Returns:
        true if actual attempt to add peer occurs

    - #### allowedUploaders

          public int allowedUploaders()

      Return number of allowed uploaders for this torrent. * Check with Snark to see if we are over the total upload limit.

    - #### banWebPeer

          public void banWebPeer(String host,
                                 boolean isPermanent)

      Ban a web peer for this torrent, for while or permanently.
      Parameters:
        `host` - the host name

    - #### completed

          public boolean completed()

    - #### connected

          public void connected(Peer peer)

      Description copied from interface: `PeerListener`
      Called when the connection to the peer has started and the handshake was successfull.
      Specified by:
        `connected` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that just got connected.

    - #### decrementUploaders

          public void decrementUploaders(boolean isInterested)

      Decrement the uploaders and (if set) the interestedUploaders counts

    - #### disconnected

          public void disconnected(Peer peer)

      Description copied from interface: `PeerListener`
      Called when the connection to the peer was terminated or the connection handshake failed.
      Specified by:
        `disconnected` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that just got disconnected.

    - #### downloaded

          public void downloaded(int size)

      Called when a peer has downloaded some bytes of a piece.
      Specified by:
        `downloaded` in interface `BandwidthListener`
      Parameters:
        `size` - the number of bytes received

    - #### getCurrentUploadRate

          public long getCurrentUploadRate()

      Returns the rate in Bps over last complete CHECK_PERIOD seconds

    - #### getDownBWLimit

          public long getDownBWLimit()

      Current limit in Bps
      Specified by:
        `getDownBWLimit` in interface `BandwidthListener`
      Returns:
        the download limit in Bps

    - #### getDownloaded

          public long getDownloaded()

      Returns the total number of downloaded bytes of all peers.

    - #### getDownloadRate

          public long getDownloadRate()

      Returns the average rate in Bps over last RATE_DEPTH * CHECK_PERIOD seconds
      Specified by:
        `getDownloadRate` in interface `BandwidthListener`
      Returns:
        the average download rate in Bps

    - #### getID

          public byte[] getID()

    - #### getInfoHash

          public byte[] getInfoHash()

    - #### getInterestedAndChoking

          public int getInterestedAndChoking()

      Returns:
        current

    - #### getInterestedUploaders

          public int getInterestedUploaders()

      Uploaders, interested only. Use this to calculate the global total, so that unchoked but uninterested peers don't count against the global limit.
      Returns:
        current

    - #### getLeft

          public long getLeft()

      Bytes not yet in storage. Does NOT account for skipped files. Returns how many bytes are still needed to get the complete torrent.
      Returns:
        -1 if in magnet mode

    - #### getMetaInfo

          public MetaInfo getMetaInfo()

    - #### getName

          public String getName()

    - #### getNeededLength

          public long getNeededLength()

      Bytes still wanted. DOES account for skipped files.
      Returns:
        exact value. or -1 if no storage yet.

    - #### getPartialPiece

          public PartialPiece getPartialPiece(Peer peer,
                                              BitField havePieces)

      Return partial piece to the PeerState if it's still wanted and peer has it.
      Specified by:
        `getPartialPiece` in interface `PeerListener`
      Parameters:
        `havePieces` - pieces the peer has, the rv will be one of these
      Returns:
        PartialPiece or null

    - #### getPeerCount

          public int getPeerCount()

      might be wrong

    - #### getPeers

          public int getPeers()

      should be right

    - #### getPEXPeers

          Set<PeerID> getPEXPeers()

      Called by TrackerClient
      Returns:
        the Set itself, modifiable, not a copy, caller should clear()

    - #### getRate

          static long getRate(long[] array)

    - #### getStorage

          public Storage getStorage()

    - #### getUpBWLimit

          public long getUpBWLimit()

      Current limit in Bps
      Specified by:
        `getUpBWLimit` in interface `BandwidthListener`
      Returns:
        the upload limit in Bps

    - #### getUploaded

          public long getUploaded()

      Returns the total number of uploaded bytes of all peers.

    - #### getUploaders

          public int getUploaders()

      Uploaders whether interested or not Use this for per-torrent limits.
      Returns:
        current

    - #### getUploadRate

          public long getUploadRate()

      Returns the average rate in Bps over last RATE_DEPTH * CHECK_PERIOD seconds
      Specified by:
        `getUploadRate` in interface `BandwidthListener`
      Returns:
        the average upload rate in Bps

    - #### getUtil

          public I2PSnarkUtil getUtil()

      Convenience
      Specified by:
        `getUtil` in interface `PeerListener`

    - #### gotBitField

          public boolean gotBitField(Peer peer,
                                     BitField bitfield)

      Returns true if the given bitfield contains at least one piece we are interested in.
      Specified by:
        `gotBitField` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `bitfield` - a BitField containing the pieces that the other side has.
      Returns:
        true when the BitField contains pieces we want, false if the piece is already known.

    - #### gotChoke

          public void gotChoke(Peer peer,
                               boolean choke)

      this does nothing but logging
      Specified by:
        `gotChoke` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `choke` - true when the peer got a choke message, false when the peer got an unchoke message.

    - #### gotCommentReq

          public void gotCommentReq(Peer peer,
                                    int num)

      Called when comments are requested via ut_comment
      Specified by:
        `gotCommentReq` in interface `PeerListener`

    - #### gotComments

          public void gotComments(Peer peer,
                                  List<Comment> comments)

      Called when comments are received via ut_comment
      Specified by:
        `gotComments` in interface `PeerListener`
      Parameters:
        `comments` - non-null

    - #### gotExtension

          public void gotExtension(Peer peer,
                                   int id,
                                   byte[] bs)

      PeerListener callback
      Specified by:
        `gotExtension` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `id` - the message ID
        `bs` - the message payload

    - #### gotHave

          public boolean gotHave(Peer peer,
                                 int piece)

      Description copied from interface: `PeerListener`
      Called when a have piece message is received. If the method returns true and the peer has not yet received a interested message or we indicated earlier to be not interested then an interested message will be send.
      Specified by:
        `gotHave` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `piece` - the piece number that the per just got.
      Returns:
        true if we still want the given piece

    - #### gotInterest

          public void gotInterest(Peer peer,
                                  boolean interest)

      Description copied from interface: `PeerListener`
      Called when an interested message is received.
      Specified by:
        `gotInterest` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `interest` - true when the peer got a interested message, false when the peer got an uninterested message.

    - #### gotPeers

          public void gotPeers(Peer peer,
                               List<PeerID> peers)

      Get peers from PEX - PeerListener callback
      Specified by:
        `gotPeers` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the message.
        `peers` - the peer IDs (dest hashes)

    - #### gotPiece

          public boolean gotPiece(Peer peer,
                                  PartialPiece pp)

      Returns false if the piece is no good (according to the hash). In that case the peer that supplied the piece should probably be blacklisted.
      Specified by:
        `gotPiece` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that got the piece.
        `pp` - the piece received.
      Returns:
        true when the bytes represent the piece, false otherwise.
      Throws:
        `RuntimeException` - on IOE saving the piece

    - #### gotPort

          public void gotPort(Peer peer,
                              int port,
                              int rport)

      PeerListener callback Tell the DHT to ping it, this will get back the node info
      Specified by:
        `gotPort` in interface `PeerListener`
      Parameters:
        `rport` - must be port + 1
        `peer` - the Peer that got the message.
        `port` - the query port

    - #### gotRequest

          public ByteArray gotRequest(Peer peer,
                                      int piece,
                                      int off,
                                      int len)

      Returns a byte array containing the requested piece or null of the piece is unknown.
      Specified by:
        `gotRequest` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that wants the piece.
        `piece` - the piece number requested.
        `off` - byte offset into the piece.
        `len` - length of the chunk requested.
      Returns:
        bytes or null for errors such as not having the piece yet
      Throws:
        `RuntimeException` - on IOE getting the data

    - #### halt

          public void halt()

    - #### halted

          public boolean halted()

    - #### isWebPeerBanned

          public boolean isWebPeerBanned(String host)

      Is a web peer banned?
      Parameters:
        `host` - the host name

    - #### needOutboundPeers

          public boolean needOutboundPeers()

      Outbound. Not halted, peers < max, and need pieces.

    - #### needPeers

          public boolean needPeers()

      Inbound. Not halted, peers < max.

    - #### needPiece

          public boolean needPiece(Peer peer,
                                   BitField havePieces)

      Called when we are downloading from the peer and may need to ask for a new piece. Returns true if wantPiece() or getPartialPiece() would return a piece.
      Specified by:
        `needPiece` in interface `PeerListener`
      Parameters:
        `peer` - the Peer that will be asked to provide the piece.
        `havePieces` - a BitField containing the pieces that the other side has.
      Returns:
        if we want any of what the peer has

    - #### overDownBWLimit

          public boolean overDownBWLimit()

      Are we currently over the limit?
      Specified by:
        `overDownBWLimit` in interface `BandwidthListener`
      Returns:
        true if over the download limit, false otherwise

    - #### overUpBWLimit

          public boolean overUpBWLimit()

      Is snark as a whole over its limit?
      Specified by:
        `overUpBWLimit` in interface `BandwidthListener`
      Returns:
        true if over the upload limit, false otherwise

    - #### overUpBWLimit

          public boolean overUpBWLimit(long total)

      Is a particular peer who has downloaded this many bytes from us in the last CHECK_PERIOD over its limit?

    - #### peerList

          public List<Peer> peerList()

      for web page detailed stats

    - #### restart

          public void restart()

    - #### savePartialPieces

          public void savePartialPieces(Peer peer,
                                        List<Request> partials)

      Save partial pieces on peer disconnection and hopefully restart it later. Replace a partial piece in the List if the new one is bigger. Storage method is private so we can expand to save multiple partials if we wish.

      Also mark the piece unrequested if this peer was the only one.

      Specified by:
        `savePartialPieces` in interface `PeerListener`
      Parameters:
        `peer` - partials, must include the zero-offset (empty) ones too. No dup pieces. len field in Requests is ignored.

    - #### sendCommentReq

          void sendCommentReq(Peer peer)

      Send a commment request message to the peer, if he supports it.

    - #### sendDHT

          void sendDHT(Peer peer)

      Send a DHT message to the peer, if we both support DHT.

    - #### sendPeers

          void sendPeers(Peer peer)

      Send a PEX message to the peer, if he supports PEX. This sends everybody we have connected to since the last time we sent PEX to him.

    - #### setRate

          static void setRate(long val,
                              long[] array)

    - #### setRateHistory

          public void setRateHistory(long up,
                                     long down)

      Push the total uploaded/downloaded onto a RATE_DEPTH deep stack

    - #### setStorage

          public void setStorage(Storage stg)

      Sets the storage after transition out of magnet mode Snark calls this after we call gotMetaInfo()

    - #### setUploaded

          public void setUploaded(long up)

      Sets the initial total of uploaded bytes of all peers (from a saved status)

    - #### setUploaders

          public void setUploaders(int upl,
                                   int inter)

      Set the uploaders and interestedUploaders counts
      Parameters:
        `upl` - whether interested or not
        `inter` - interested only

    - #### setWantedPieces

          public void setWantedPieces()

      Only called externally from Storage after the double-check fails. Sets wantedBytes too.

    - #### shouldRequest

          public boolean shouldRequest(Peer peer,
                                       int size)

      Should we request this many bytes?
      Specified by:
        `shouldRequest` in interface `BandwidthListener`
      Parameters:
        `peer` - the peer to request from
        `size` - the number of bytes to request
      Returns:
        true if the request should be made, false otherwise

    - #### shouldSend

          public boolean shouldSend(int size)

      Should we send this many bytes? Do NOT call uploaded() if this returns true.
      Specified by:
        `shouldSend` in interface `BandwidthListener`
      Parameters:
        `size` - the number of bytes to send
      Returns:
        true if the bytes should be sent, false otherwise

    - #### unchokePeer

          void unchokePeer()

      (Optimistically) unchoke. Must be called with peers synchronized

    - #### updatePiecePriorities

          public void updatePiecePriorities()

      Maps file priorities to piece priorities. Call after updating file priorities Storage.setPriority()

    - #### uploaded

          public void uploaded(int size)

      Called when a peer has uploaded some bytes of a piece.
      Specified by:
        `uploaded` in interface `BandwidthListener`
      Parameters:
        `size` - the number of bytes sent