java.lang.Object
org.klomp.snark.Peer
All Implemented Interfaces:
Comparable<Peer>, BandwidthListener
Direct Known Subclasses:
WebPeer

public class Peerextends Object implements Comparable<Peer>, BandwidthListener
Represents a peer in the BitTorrent network, managing all public information and communication with that peer.

This class handles:

  • Peer identification and handshaking
  • BitTorrent protocol communication
  • Upload/download statistics tracking
  • Connection state management
  • Bandwidth monitoring and limiting

Peers can be either incoming (they connected to us) or outgoing (we connected to them) connections.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private static final AtomicLong
    Counter numbering the peer objects.
    private final AtomicBoolean
    True once the peer has been disconnected.
    private final DontHaveMemo
    Pieces the peer told us it does not have.
    private final long
    This peer's id, for the logs.
    private final boolean
    True if we accepted the connection.
    protected final Log
    Logger for this peer instance.
    private int
    Maximum pipelined requests for the peer.
    private int
    Comments sent to the peer so far.
    private boolean
    True if the peer only receives uploads.
    (package private) static final long
    How often to recompute upload slots and UI download speeds.
    private long
    When the peer connected.
    private boolean
    True to deregister the peer when it disconnects.
    Stream reading the data from the peer.
    Stream writing the data to the peer.
    private final AtomicLong
    Running counters.
    private final long[]
    Download totals at the rate stat boundaries.
    private static final byte[]
    Protocol name sent in the handshake.
    private static final long
    Bound on the socket read timeout while reading the handshake, to avoid hanging a thread on a stalled peer.
    private Map<String,BEValue>
    The handshake map received from the peer.
    protected final byte[]
    SHA-1 of this torrent's info dictionary, sent in the handshake and checked against the remote copy.
    (package private) MagnetState
    Shared across all peers on this torrent.
    protected MetaInfo
    Will start out null in magnet mode.
    private final byte[]
    Our 20-byte peer id.
    private static final long
    Extension protocol option bit.
    private static final long
    Fast extension option bit.
    private long
    Options negotiated with the peer.
    private final PeerID
    The remote peer's id.
    private long
    When PEX was last sent to the peer.
    (package private) static final int
    Number of samples in the upload/download rate windows.
    private I2PSocket
    The socket connected to the peer.
    (package private) PeerState
    ` Keeps state for in/out connections.
    private final AtomicLong
    Bytes uploaded to this peer.
    private final long[]
    Upload totals at the rate stat boundaries.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Peer(I2PSocket sock, InputStream in, OutputStream out, byte[] myId, byte[] infohash, MetaInfo metainfo)
    Incoming connection.
    Peer(PeerID peerID, byte[] myId, byte[] infohash, MetaInfo metainfo)
    Outgoing connection.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addDontHave(int piece)
    Remember that this peer said it no longer has the piece (BEP 54 dont_have), so it is not re-requested from this peer until the remembered state expires, even if it re-announces HAVE.
    (package private) void
    cancel(int piece)
    Tell the other side that we are no longer interested in any of the outstanding requests (if any) for this piece.
    int
    Compares this peer to another by PeerID, then by internal ID.
    int
    Return how much the peer has.
    (package private) void
    Disconnects this peer, closing the socket and cleaning up state.
    void
    disconnect(boolean deregister)
    Disconnects this peer if it was connected.
    boolean
    dontHave(int piece)
    Whether this peer recently said it does not have the piece (BEP 54 dont_have).
    void
    downloaded(int size)
    BandwidthListener interface - increments the download counter for this peer.
    boolean
    Two Peers are equal when they have the same PeerID.
    Returns the I2P destination of the connected peer.
    long
    Current limit in Bps.
    long
    Returns the number of bytes that have been downloaded.
    long
    Returns the average rate in Bps.
    Returns the extension handshake data from the peer.
    (package private) I2PSocket
    The I2PSocket of this connection, or null if never connected.
    long
    Returns the time in milliseconds since the last activity on either the incoming or outgoing connection.
    Returns the shared magnet state across all peers on this torrent.
    long
    The max inactive time.
    int
    Returns the maximum request pipeline depth negotiated with the peer.
    This peer's ID, assigned when it was created.
    long
    Returns the timestamp when PEX peers were last sent to this peer.
    Socket debug string, for debug printing.
    (package private) int
    Returns the total number of UT comments sent to this peer.
    long
    Current limit in Bps.
    long
    Returns the number of bytes that have been uploaded.
    long
    Returns the average rate in Bps.
    long
    Returns the timestamp when the handshake completed.
    private byte[]
    Perform the handshake over the data streams and return the ID reported by the other side.
    int
    The hash code of a Peer is the hash code of the peerID.
    void
    have(int piece)
    Notifies the peer that we have a new piece.
    boolean
    Whether or not the peer choked us.
    boolean
    Whether or not we are choking the peer.
    boolean
    Return if a peer is a seeder.
    boolean
    Returns whether the peer is in active communication.
    boolean
    Returns whether this is an incoming connection (initiated by the remote peer).
    boolean
    Whether or not the peer is interested in pieces we have.
    boolean
    Whether or not the peer has pieces we want from it.
    boolean
    Whether or not the peer is snubbing us (unchoked but not requesting).
    boolean
    Whether the peer declared itself a partial seed (BEP 21 upload_only).
    boolean
    Returns whether this is a web peer.
    void
    Send keepalive.
    boolean
    Are we currently over the limit?
    boolean
    Is snark as a whole over its limit?
    (package private) void
    Update the request queue.
    void
    Retransmit outstanding requests if necessary Does nothing if supportsFast().
    void
    runConnection(I2PSnarkUtil util, PeerListener listener, BandwidthListener bwl, BitField bitfield, MagnetState mState, boolean uploadOnly)
    Runs the connection to the other peer.
    void
    sendExtension(int type, byte[] payload)
    Sends a protocol extension message to the peer.
    void
    setChoking(boolean choke)
    Whether we are choking the peer.
    void
    Store the extension handshake data and update the request pipeline limit.
    void
    Switches from magnet mode to normal mode with metainfo.
    void
    setPexLastSent(long now)
    Store the timestamp when PEX peers were last sent to this peer.
    (package private) void
    Push the total uploaded/downloaded onto a RATE_DEPTH deep stack.
    (package private) void
    Store the total number of UT comments sent to this peer.
    void
    setUploadOnly(boolean uploadOnly)
    The peer declared itself a partial seed (BEP 21 upload_only).
    boolean
    shouldRequest(int size)
    Should we request this many bytes?
    boolean
    shouldRequest(Peer peer, int size)
    Should we request this many bytes? Only true for this peer, which is how a callback that was handed one peer is prevented from answering for another.
    boolean
    shouldSend(int size)
    Should we send this many bytes? Do NOT call uploaded after this.
    boolean
    Returns whether the peer supports the FAST protocol extension (BEP 6).
    Returns the String representation of the peerID.
    void
    uploaded(int size)
    Increments the upload counter for this peer.

    Methods inherited from class Object

    clone, getClass, notify, notifyAll, wait, wait, wait
  • Field Details

    • _log

      protected final Log _log
      Logger for this peer instance.
    • peerID

      private final PeerID peerID
      The remote peer's id.
    • my_id

      private final byte[] my_id
      Our 20-byte peer id.
    • infohash

      protected final byte[] infohash
      SHA-1 of this torrent's info dictionary, sent in the handshake and checked against the remote copy.
    • metainfo

      protected MetaInfo metainfo
      Will start out null in magnet mode.
    • handshakeMap

      private Map<String,BEValue> handshakeMap
      The handshake map received from the peer.
    • din

      private DataInputStream din
      Stream reading the data from the peer.
    • dout

      private DataOutputStream dout
      Stream writing the data to the peer.
    • downloaded

      private final AtomicLong downloaded
      Running counters.
    • uploaded

      private final AtomicLong uploaded
      Bytes uploaded to this peer.
    • state

      volatile PeerState state
      ` Keeps state for in/out connections. Non-null when the handshake was successful, the connection setup and runs. Do not access directly. All actions should be through Peer methods.
    • magnetState

      MagnetState magnetState
      Shared across all peers on this torrent.
    • sock

      private I2PSocket sock
      The socket connected to the peer.
    • deregister

      private boolean deregister
      True to deregister the peer when it disconnects.
    • __id

      private static final AtomicLong __id
      Counter numbering the peer objects.
    • _id

      private final long _id
      This peer's id, for the logs.
    • _disconnected

      private final AtomicBoolean _disconnected
      True once the peer has been disconnected.
    • CHECK_PERIOD

      static final long CHECK_PERIOD
      How often to recompute upload slots and UI download speeds.
      See Also:
    • RATE_DEPTH

      static final int RATE_DEPTH
      Number of samples in the upload/download rate windows.
      See Also:
    • uploaded_old

      private final long[] uploaded_old
      Upload totals at the rate stat boundaries.
    • downloaded_old

      private final long[] downloaded_old
      Download totals at the rate stat boundaries.
    • HANDSHAKE

      private static final byte[] HANDSHAKE
      Protocol name sent in the handshake.
    • OPTION_EXTENSION

      private static final long OPTION_EXTENSION
      Extension protocol option bit.
      See Also:
    • OPTION_FAST

      private static final long OPTION_FAST
      Fast extension option bit.
      See Also:
    • options

      private long options
      Options negotiated with the peer.
    • _isIncoming

      private final boolean _isIncoming
      True if we accepted the connection.
    • _uploadOnly

      private volatile boolean _uploadOnly
      True if the peer only receives uploads.
    • _dontHave

      private final DontHaveMemo _dontHave
      Pieces the peer told us it does not have.
    • _totalCommentsSent

      private int _totalCommentsSent
      Comments sent to the peer so far.
    • _maxPipeline

      private int _maxPipeline
      Maximum pipelined requests for the peer.
    • connected

      private long connected
      When the peer connected.
    • pexLastSent

      private long pexLastSent
      When PEX was last sent to the peer.
    • HANDSHAKE_READ_TIMEOUT

      private static final long HANDSHAKE_READ_TIMEOUT
      Bound on the socket read timeout while reading the handshake, to avoid hanging a thread on a stalled peer.
      See Also:
  • Constructor Details

    • Peer

      public Peer(PeerID peerID, byte[] myId, byte[] infohash, MetaInfo metainfo)
      Outgoing connection. Creates a disconnected peer given a PeerID, your own id and the relevant MetaInfo.
      Parameters:
      peerID - the remote peer's ID
      myId - our own peer ID, sent during the handshake
      infohash - the info hash of the torrent
      metainfo - null if in magnet mode
    • Peer

      public Peer(I2PSocket sock, InputStream in, OutputStream out, byte[] myId, byte[] infohash, MetaInfo metainfo) throws IOException
      Incoming connection. Creates a unconnected peer from the input and output stream got from the socket. Note that the complete handshake (which can take some time or block indefinitely) is done in the calling Thread to get the remote peer id. To completely start the connection call the connect() method.
      Parameters:
      sock - the connected socket the handshake runs over
      in - the stream the handshake is read from
      out - the stream the handshake is written to
      myId - our own peer ID, sent during the handshake
      infohash - the info hash of the torrent
      metainfo - null if in magnet mode
      Throws:
      IOException - when an error occurred during the handshake.
  • Method Details

    • isIncoming

      public boolean isIncoming()
      Returns whether this is an incoming connection (initiated by the remote peer).
      Returns:
      true if incoming, false if outgoing
    • setUploadOnly

      public void setUploadOnly(boolean uploadOnly)
      The peer declared itself a partial seed (BEP 21 upload_only).

      A partial seed has some files but does not want to download anything more.

      Parameters:
      uploadOnly - whether the peer is a partial seed
    • isUploadOnly

      public boolean isUploadOnly()
      Whether the peer declared itself a partial seed (BEP 21 upload_only).

      A partial seed has some files but does not want to download anything more.

      Returns:
      true if the peer is a partial seed
    • addDontHave

      public void addDontHave(int piece)
      Remember that this peer said it no longer has the piece (BEP 54 dont_have), so it is not re-requested from this peer until the remembered state expires, even if it re-announces HAVE.
      Parameters:
      piece - the piece index
    • dontHave

      public boolean dontHave(int piece)
      Whether this peer recently said it does not have the piece (BEP 54 dont_have).
      Parameters:
      piece - the piece index
      Returns:
      true if within the retention window
    • getPeerID

      public PeerID getPeerID()
      This peer's ID, assigned when it was created.
      Returns:
      the peer ID this peer was created with
    • toString

      public String toString()
      Returns the String representation of the peerID.
      Overrides:
      toString in class Object
    • getSocket

      public String getSocket()
      Socket debug string, for debug printing.
      Returns:
      socket debug string (for debug printing)
    • getI2PSocket

      I2PSocket getI2PSocket()
      The I2PSocket of this connection, or null if never connected.
      Returns:
      the socket backing this connection, or null if never connected
    • hashCode

      public int hashCode()
      The hash code of a Peer is the hash code of the peerID.
      Overrides:
      hashCode in class Object
    • equals

      public boolean equals(Object o)
      Two Peers are equal when they have the same PeerID. All other properties are ignored.
      Overrides:
      equals in class Object
    • compareTo

      public int compareTo(Peer p)
      Compares this peer to another by PeerID, then by internal ID.
      Specified by:
      compareTo in interface Comparable<Peer>
      Parameters:
      p - the peer to compare to
      Returns:
      a negative, zero, or positive integer
    • runConnection

      public void runConnection(I2PSnarkUtil util, PeerListener listener, BandwidthListener bwl, BitField bitfield, MagnetState mState, boolean uploadOnly)
      Runs the connection to the other peer. This method does not return until the connection is terminated.

      When the connection is correctly started the connected() method of the given PeerListener is called. If the connection ends or the connection could not be setup correctly the disconnected() method is called.

      If the given BitField is non-null it is send to the peer as first message.

      Parameters:
      util - the utility holding this client's connection settings
      listener - notified when the connection is established or ends
      bwl - the bandwidth limiter this peer's traffic is accounted against
      bitfield - the bitfield sent as the first message, or null to send none
      mState - the magnet state recording this peer's piece availability
      uploadOnly - if we are complete with skipped files, i.e. a partial seed
    • handshake

      private byte[] handshake(InputStream in, OutputStream out) throws IOException
      Perform the handshake over the data streams and return the ID reported by the other side.
      Parameters:
      in - the socket input stream to read the peer's handshake from
      out - the socket output stream to write our handshake to
      Returns:
      the 20-byte peer ID reported by the other side
      Throws:
      IOException - if the peer's handshake is malformed or its metainfo hash does not match ours
    • supportsFast

      public boolean supportsFast()
      Returns whether the peer supports the FAST protocol extension (BEP 6).
      Returns:
      true if FAST extension is supported
    • getDestination

      public Destination getDestination()
      Returns the I2P destination of the connected peer.
      Returns:
      the peer's Destination, or null if not connected
    • getMagnetState

      public MagnetState getMagnetState()
      Returns the shared magnet state across all peers on this torrent. Callers must synchronize on the returned object.
      Returns:
      the magnet state (non-null)
    • getHandshakeMap

      public Map<String,BEValue> getHandshakeMap()
      Returns the extension handshake data from the peer.
      Returns:
      the handshake map, or null if not yet received
    • setHandshakeMap

      public void setHandshakeMap(Map<String,BEValue> map)
      Store the extension handshake data and update the request pipeline limit.
      Parameters:
      map - the handshake map (non-null)
    • getMaxPipeline

      public int getMaxPipeline()
      Returns the maximum request pipeline depth negotiated with the peer.
      Returns:
      the max pipeline depth, bounded by MIN_PIPELINE and MAX_PIPELINE
    • sendExtension

      public void sendExtension(int type, byte[] payload)
      Sends a protocol extension message to the peer.
      Parameters:
      type - the extension message type
      payload - the extension message payload
    • setMetaInfo

      public void setMetaInfo(MetaInfo meta)
      Switches from magnet mode to normal mode with metainfo.
      Parameters:
      meta - the metainfo to set
    • isConnected

      public boolean isConnected()
      Returns whether the peer is in active communication.
      Returns:
      true if the peer has an active PeerState
    • disconnect

      public void disconnect(boolean deregister)
      Disconnects this peer if it was connected. PeerListener.disconnected(Peer) will be called when the connection is completely terminated. If deregister is true, partial pieces will be returned.
      Parameters:
      deregister - true if partial pieces should be returned
    • disconnect

      void disconnect()
      Disconnects this peer, closing the socket and cleaning up state. Partial pieces are returned if deregister is true.
    • have

      public void have(int piece)
      Notifies the peer that we have a new piece.
      Parameters:
      piece - the piece index
    • cancel

      void cancel(int piece)
      Tell the other side that we are no longer interested in any of the outstanding requests (if any) for this piece.
      Parameters:
      piece - the piece index
    • request

      void request()
      Update the request queue. Call after adding wanted pieces.
    • isInterested

      public boolean isInterested()
      Whether or not the peer is interested in pieces we have.
      Returns:
      true if interested, false if not connected
    • isInteresting

      public boolean isInteresting()
      Whether or not the peer has pieces we want from it.
      Returns:
      true if interesting, false if not connected
    • setChoking

      public void setChoking(boolean choke)
      Whether we are choking the peer. Defaults to true. When choke is false and the peer requests some pieces we upload them, otherwise requests of this peer are ignored.
      Parameters:
      choke - true to choke (stop uploading), false to unchoke (allow uploads)
    • isChoking

      public boolean isChoking()
      Whether or not we are choking the peer.
      Returns:
      true when choking or not connected
    • isChoked

      public boolean isChoked()
      Whether or not the peer choked us.
      Returns:
      true when choked or not connected
    • isSnubbing

      public boolean isSnubbing()
      Whether or not the peer is snubbing us (unchoked but not requesting).
      Returns:
      true if snubbing, false if not connected
    • downloaded

      public void downloaded(int size)
      BandwidthListener interface - increments the download counter for this peer.
      Specified by:
      downloaded in interface BandwidthListener
      Parameters:
      size - the number of bytes downloaded
    • uploaded

      public void uploaded(int size)
      Increments the upload counter for this peer.
      Specified by:
      uploaded in interface BandwidthListener
      Parameters:
      size - the number of bytes uploaded
    • getDownloaded

      public long getDownloaded()
      Returns the number of bytes that have been downloaded. Can be reset to zero with resetCounters() which is called every CHECK_PERIOD by PeerCheckerTask.
      Returns:
      the downloaded byte count since the last counter reset
    • getUploaded

      public long getUploaded()
      Returns the number of bytes that have been uploaded. Can be reset to zero with resetCounters() which is called every CHECK_PERIOD by PeerCheckerTask.
      Returns:
      the uploaded byte count since the last counter reset
    • getUploadRate

      public long getUploadRate()
      Returns the average rate in Bps.
      Specified by:
      getUploadRate in interface BandwidthListener
      Returns:
      average upload rate in bytes per second
    • getDownloadRate

      public long getDownloadRate()
      Returns the average rate in Bps.
      Specified by:
      getDownloadRate in interface BandwidthListener
      Returns:
      average download rate in bytes per second
    • shouldSend

      public boolean shouldSend(int size)
      Should we send this many bytes? Do NOT call uploaded after this.
      Specified by:
      shouldSend in interface BandwidthListener
      Parameters:
      size - the number of bytes to send
      Returns:
      true if the bandwidth listener allows the send, which is then counted
    • shouldRequest

      public boolean shouldRequest(int size)
      Should we request this many bytes?
      Parameters:
      size - the number of bytes about to be requested
      Returns:
      true if the bandwidth listener allows this peer to request them
    • shouldRequest

      public boolean shouldRequest(Peer peer, int size)
      Should we request this many bytes? Only true for this peer, which is how a callback that was handed one peer is prevented from answering for another.
      Specified by:
      shouldRequest in interface BandwidthListener
      Parameters:
      peer - the peer the callback was invoked for
      size - the requested size in bytes
      Returns:
      true only if peer is this peer and the listener allows the request
    • getUpBWLimit

      public long getUpBWLimit()
      Current limit in Bps.
      Specified by:
      getUpBWLimit in interface BandwidthListener
      Returns:
      the up b w limit
    • 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
    • getDownBWLimit

      public long getDownBWLimit()
      Current limit in Bps.
      Specified by:
      getDownBWLimit in interface BandwidthListener
      Returns:
      the down b w limit
    • 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
    • setRateHistory

      void setRateHistory()
      Push the total uploaded/downloaded onto a RATE_DEPTH deep stack. Resets the downloaded and uploaded counters to zero.
    • getInactiveTime

      public long getInactiveTime()
      Returns the time in milliseconds since the last activity on either the incoming or outgoing connection.
      Returns:
      inactive time in ms, or -1 if not connected
    • getMaxInactiveTime

      public long getMaxInactiveTime()
      The max inactive time.
      Returns:
      inactivity limit in ms before the peer is disconnected
    • keepAlive

      public void keepAlive()
      Send keepalive.
    • retransmitRequests

      public void retransmitRequests()
      Retransmit outstanding requests if necessary Does nothing if supportsFast().
    • completed

      public int completed()
      Return how much the peer has.
      Returns:
      number of completed pieces (not bytes)
    • isCompleted

      public boolean isCompleted()
      Return if a peer is a seeder.
      Returns:
      true if the peer has all pieces
    • getTotalCommentsSent

      int getTotalCommentsSent()
      Returns the total number of UT comments sent to this peer.
      Returns:
      the comment count
    • setTotalCommentsSent

      void setTotalCommentsSent(int count)
      Store the total number of UT comments sent to this peer.
      Parameters:
      count - the count to set
    • isWebPeer

      public boolean isWebPeer()
      Returns whether this is a web peer.
      Returns:
      false (base Peer is never a web peer)
    • getWhenConnected

      public long getWhenConnected()
      Returns the timestamp when the handshake completed.
      Returns:
      the connection timestamp in milliseconds
    • getPexLastSent

      public long getPexLastSent()
      Returns the timestamp when PEX peers were last sent to this peer.
      Returns:
      the last PEX timestamp in milliseconds
    • setPexLastSent

      public void setPexLastSent(long now)
      Store the timestamp when PEX peers were last sent to this peer.
      Parameters:
      now - the current timestamp in milliseconds