- All Implemented Interfaces:
DataLoader
This class tracks peer interest/choking status, piece availability, request queues, and handles all BitTorrent protocol message processing. Methods that inspect or change the state synchronize (on this).
Field Summary
Modifier and TypeFieldDescriptionBEP 6: pieces we advertised to the peer, servable even while choking it.private longBytes of piece data the peer cancelled after requesting.BEP 6: pieces a conforming sender may advertise to us.private longTime of the last inbound request from the peer.private final LogLogger for this class.BEP 6: pieces the peer advertised to us, requestable while it chokes us.(package private) BitFieldThe pieces the peer has.private final BandwidthListenerTold about bytes transferred with this peer.(package private) booleanWhether the peer has choked us.(package private) booleanWhether we are choking the peer.private intMost requests that may be outstanding at once.private static final intDefault cap on a single piece request from a peer.private static final intDefault maximum request pipeline depth for outbound requests.private static final intDefault minimum request pipeline depth for outbound requests.private static final intDefault chunk size for outbound piece requests.private static final intDiscard-ratio auto-ban: cancelled percentage above which the peer is banned.private static final longDiscard-ratio auto-ban: minimum cancelled+uploaded bytes before evaluation.Null unless needed.(package private) final PeerConnectionInInbound connection.(package private) booleanWhether the peer is interested in us.(package private) booleanWhether we are interested in the peer.private longLast time a choke message was sent.private RequestThe tail (NOT the head) of the request queue.(package private) final PeerListenerFixme, used byPeer.disconnect()to get to the coordinator.private static final byte[]The four zero bytes libtorrent uses in place of an IPv4 address when its remote peer is an I2P destination; shared read-only.static intCap on a single piece request from a peer.static intMaximum request pipeline depth for outbound requests.private static final intAbsolute cap on pipeline depth, outbound or inbound.private MetaInfoNull before we have it.static intMinimum request pipeline depth for outbound requests.(package private) final PeerConnectionOutOutbound connection.Piece requests sent but not yet answered.static intChunk size for outbound piece requests.private final PeerThe peer this state describes.private RequestFlag to back up from firstOutstandingRequest when calculating progress.private static final IntegerSentinel meaning every piece rather than one.private static final longAnti-snub: unchoked peers that send no request for this long are snubbing.Constructor Summary
ConstructorsConstructorDescriptionPeerState(Peer peer, PeerListener listener, BandwidthListener bwl, MetaInfo metainfo, PeerConnectionIn in, PeerConnectionOut out) Create the peer state for the torrent.Method Summary
Modifier and TypeMethodDescription(package private) voidAdds a new request to the outstanding requests list.(package private) voidallowedFastMessage(int piece) Handle an allowed fast message (BEP 6): the peer will serve this piece even while it chokes us.(package private) voidbitfieldMessage(byte[] bitmap) Handle a bitfield message from the peer.private voidbitfieldMessage(byte[] bitmap, boolean isAll) Mark the peer's bitfield.(package private) voidcancelMessage(int piece, int begin, int length) Handle a cancel message from the peer.(package private) voidcancelPiece(int piece) Tell the other side that we are no longer interested in any of the outstanding requests (if any) for this piece.private voidcheckDiscardRatio(int length) Count cancelled bytes and ban the peer if it cancels most of what it requests.(package private) voidchokeMessage(boolean choke) Handle a choke or unchoke message from the peer.(package private) voiddontHaveMessage(int piece) Handle a BEP 54 lt_donthave message: the peer no longer has a piece it advertised.The set of piece indices a conforming sender may advertise to us, or null when nothing can be derived.(package private) voidextensionMessage(int id, byte[] bs) Handle an extension message from the peer.generateAllowedFastSet(byte[] peerHash, byte[] infohash, int pieces) Generate the BEP 6 allowed fast set for a peer.(package private) BandwidthListenerReturns the bandwidth listener for this peer state.private intgetFirstOutstandingRequest(int piece) Finds the first outstanding request for a piece.private RequestgetLowestOutstandingRequest(int piece) Finds the outstanding request for the piece with the lowest offset.(package private) RequestgetOutstandingRequest(int piece, int begin, int length) Called when a piece message is being processed by the incoming connection.private intEffective request pipeline depth for this peer.All pieces we are currently requesting, or an empty set.(package private) StringDebug string for the peer, or null.(package private) voidhaveMessage(boolean isAll) BEP 6.(package private) voidhaveMessage(int piece) Handle a have message from the peer.(package private) voidhavePiece(int piece) We now have this piece.(package private) voidinterestedMessage(boolean interest) Handle an interested or uninterested message from the peer.(package private) booleanisAllowedFast(int piece) Whether the piece is in our BEP 6 allowed fast set for this peer.(package private) booleanTrue if the peer is snubbing us: it is unchoked by us and interested but has sent no request for SNUB_TIMEOUT, and nothing is queued to send it.(package private) voidSend a keepalive message to the peer.(package private) voidHandle a keepalive message from the peer.loadData(int piece, int begin, int length) This is the callback that PeerConnectionOut calls.static intMaximum bytes of pipelined inbound requests to a single peer.(package private) voidpieceMessage(Request req) Called when a full chunk (i.e.(package private) voidportMessage(int port) Handle a port message from the peer.(package private) voidrejectMessage(int piece, int begin, int length) BEP 6.private voidrequest(boolean resend) Starts or resumes requesting pieces.(package private) voidrequestMessage(int piece, int begin, int length) Handle a request message from the peer.private booleanrequestNextPiece(Set<Integer> allowed) Starts requesting first chunk of next piece.(package private) voidRetransmit outstanding requests if necessary.Return partial pieces to the PeerCoordinator and clear the request queue.returnPartialPieces(int piece) Return the partial piece for a piece to the PeerCoordinator.(package private) voidsendAllowedFast(BitField ourBitfield) Generate and queue the BEP 6 allowed fast set for this peer.(package private) voidsetChoking(boolean choke) Whether we are choking the peer.(package private) voidsetInteresting(boolean interest) Whether we are interested in pieces from this peer.voidsetMetaInfo(MetaInfo meta) Switch from magnet mode to normal mode.static voidsetPipelineParams(int min, int max, int chunk, int maxPart) Apply configurable pipeline bounds, chunk size, and per-request cap, clamped to safe limits.(package private) voidsuggestMessage(int piece) BEP 6 Suggest.(package private) voidunknownMessage(int type, byte[] bs) Handle an unknown message type from the peer.(package private) voiduploaded(int size) Called when some bytes have left the outgoing connection.
Field Details
_log
Logger for this class.peer
The peer this state describes.listener
Fixme, used byPeer.disconnect()to get to the coordinator.bwListener
Told about bytes transferred with this peer.metainfo
Null before we have it. locking: this.havesBeforeMetaInfo
interesting
volatile boolean interestingWhether we are interested in the peer.choking
volatile boolean chokingWhether we are choking the peer.interested
volatile boolean interestedWhether the peer is interested in us.choked
volatile boolean chokedWhether the peer has choked us._allowedFast
_peerAllowedFast
_expectedAllowedFast
BEP 6: pieces a conforming sender may advertise to us. The union of two derivations: our own destination-hash-based set (I2PSnark peers) and the libtorrent-style set derived from a zeroed IPv4 address (qBittorrent and other libtorrent clients over I2P, whose "remote address" is 0.0.0.0 for every I2P connection). Null until computed, or when nothing can be derived.LT_I2P_ADDR_PREFIX
private static final byte[] LT_I2P_ADDR_PREFIXThe four zero bytes libtorrent uses in place of an IPv4 address when its remote peer is an I2P destination; shared read-only.bitfield
BitField bitfieldThe pieces the peer has. Locking: this.in
Inbound connection.out
Outbound connection.outstandingRequests
_cancelledBytes
private long _cancelledBytesBytes of piece data the peer cancelled after requesting._lastRequestTime
private long _lastRequestTimeTime of the last inbound request from the peer.SNUB_TIMEOUT
private static final long SNUB_TIMEOUTAnti-snub: unchoked peers that send no request for this long are snubbing. Measured from the last request, or from the last unchoke we sent.- See Also:
lastRequest
The tail (NOT the head) of the request queue.currentMaxPipeline
private int currentMaxPipelineMost requests that may be outstanding at once.DEFAULT_MIN_PIPELINE
private static final int DEFAULT_MIN_PIPELINEDefault minimum request pipeline depth for outbound requests.- See Also:
DEFAULT_MAX_PIPELINE
private static final int DEFAULT_MAX_PIPELINEDefault maximum request pipeline depth for outbound requests.- See Also:
DEFAULT_PARTSIZE
private static final int DEFAULT_PARTSIZEDefault chunk size for outbound piece requests.- See Also:
DEFAULT_MAX_PARTSIZE
private static final int DEFAULT_MAX_PARTSIZEDefault cap on a single piece request from a peer.- See Also:
DISCARD_MIN_BYTES
private static final long DISCARD_MIN_BYTESDiscard-ratio auto-ban: minimum cancelled+uploaded bytes before evaluation.- See Also:
DISCARD_BAN_PERCENT
private static final int DISCARD_BAN_PERCENTDiscard-ratio auto-ban: cancelled percentage above which the peer is banned.- See Also:
MAX_PIPELINE_CAP
private static final int MAX_PIPELINE_CAPAbsolute cap on pipeline depth, outbound or inbound.- See Also:
MIN_PIPELINE
public static volatile int MIN_PIPELINEMinimum request pipeline depth for outbound requests.MAX_PIPELINE
public static volatile int MAX_PIPELINEMaximum request pipeline depth for outbound requests. Bumped from 16 to 32 so a single peer can keep enough chunks in flight to saturate a fast connection at tunnel latency; the per-connection AIMD still dials this down under bandwidth pressure.PARTSIZE
public static volatile int PARTSIZEChunk size for outbound piece requests.MAX_PARTSIZE
public static volatile int MAX_PARTSIZECap on a single piece request from a peer.PIECE_ALL
Sentinel meaning every piece rather than one.pendingRequest
Flag to back up from firstOutstandingRequest when calculating progress.lastChokeSendTime
private long lastChokeSendTimeLast time a choke message was sent.
Constructor Details
PeerState
PeerState(Peer peer, PeerListener listener, BandwidthListener bwl, MetaInfo metainfo, PeerConnectionIn in, PeerConnectionOut out) Create the peer state for the torrent.- Parameters:
peer- the peer this state tracks, whose availability and stats drive the decisionslistener- the coordinator callback told about choke, interest, haves and requestsbwl- the bandwidth listener notified of the bytes uploaded to the peermetainfo- null if in magnet modein- the connection the peer's messages arrive onout- the connection requests and control messages are sent on
Method Details
maxPipelineBytes
public static int maxPipelineBytes()Maximum bytes of pipelined inbound requests to a single peer. Derived from MAX_PIPELINE and PARTSIZE, so it follows the configurable values.- Returns:
- the inbound pipeline byte ceiling
setPipelineParams
public static void setPipelineParams(int min, int max, int chunk, int maxPart) Apply configurable pipeline bounds, chunk size, and per-request cap, clamped to safe limits. Call once at startup; standalone snark reads these from its own config file.- Parameters:
min- minimum pipeline depth, clamped to [1, max]max- maximum pipeline depth, clamped to [min, MAX_PIPELINE_CAP]chunk- request chunk size in bytes, clamped to [1KiB, maxPart]maxPart- cap on a single request in bytes, clamped to [chunk, 1MiB]
getPipelineLimit
private int getPipelineLimit()Effective request pipeline depth for this peer. When the torrent's piece length is smaller than a chunk size, each request carries fewer bytes, so we scale the count up to keep roughly the same volume in flight. Starts from the negotiated peer reqq and is capped at the absolute pipeline limit, keeping the byte volume intact for small pieces.- Returns:
- the pipeline limit for the current torrent, at least MIN_PIPELINE
getBandwidthListener
BandwidthListener getBandwidthListener()Returns the bandwidth listener for this peer state.- Returns:
- the bandwidth listener
keepAliveMessage
void keepAliveMessage()Handle a keepalive message from the peer.chokeMessage
void chokeMessage(boolean choke) Handle a choke or unchoke message from the peer.- Parameters:
choke- true if the peer is now choking us, false if it has unchoked
interestedMessage
void interestedMessage(boolean interest) Handle an interested or uninterested message from the peer.- Parameters:
interest- true if the peer is now interested in us, false if not
haveMessage
void haveMessage(int piece) Handle a have message from the peer.- Parameters:
piece- the zero-based index of the piece being acted on
bitfieldMessage
void bitfieldMessage(byte[] bitmap) Handle a bitfield message from the peer.- Parameters:
bitmap- the peer's have bits as received, highest bit first
bitfieldMessage
private void bitfieldMessage(byte[] bitmap, boolean isAll) Mark the peer's bitfield.- Parameters:
bitmap- null to use the isAll paramisAll- only if bitmap == null: true for have_all, false for have_none
requestMessage
void requestMessage(int piece, int begin, int length) Handle a request message from the peer.- Parameters:
piece- the zero-based index of the piece being acted onbegin- the byte offset into the piece where the requested block startslength- the size of the requested block in bytes
loadData
This is the callback that PeerConnectionOut calls.- Specified by:
loadDatain interfaceDataLoader- Parameters:
piece- index of the requested piecebegin- byte offset within the piece where the request startslength- number of bytes requested- Returns:
- bytes or null for errors such as not having the piece yet
- Throws:
RuntimeException- on IOE getting the data
uploaded
void uploaded(int size) Called when some bytes have left the outgoing connection. Does not indicate whether the bytes were a real piece or overhead.- Parameters:
size- the number of bytes just sent to the peer
isSnubbing
boolean isSnubbing()True if the peer is snubbing us: it is unchoked by us and interested but has sent no request for SNUB_TIMEOUT, and nothing is queued to send it. The clock restarts when we unchoke the peer, so a freshly unchoked peer always gets the full window to respond.- Returns:
- true if the peer is snubbing
pieceMessage
Called when a full chunk (i.e. a piece message) has been received byPeerConnectionIn.This may block quite a while if it is the last chunk for a piece, as it calls the listener, who stores the piece and then calls havePiece for every peer on the torrent (including us).
- Parameters:
req- the request satisfied by the block that just arrived
getFirstOutstandingRequest
private int getFirstOutstandingRequest(int piece) Finds the first outstanding request for a piece.- Parameters:
piece- the piece number asked about, or PIECE_ALL- Returns:
- its position, -1 if there is none
getOutstandingRequest
Called when a piece message is being processed by the incoming connection. That is, when the header of the piece message was received. Returns null when there was no such request. It also requeues/sends requests when it thinks that they must have been lost.- Parameters:
piece- the zero-based index of the piece being acted onbegin- the byte offset into the piece where the received block startslength- the size of the received block in bytes- Returns:
- the outstanding request matching that chunk, or null if none was sent
getLowestOutstandingRequest
Finds the outstanding request for the piece with the lowest offset.- Parameters:
piece- the piece number to look for among the outstanding requests- Returns:
- the request, null if none outstanding for the piece
returnPartialPieces
returnPartialPieces
getRequestedPieces
cancelMessage
void cancelMessage(int piece, int begin, int length) Handle a cancel message from the peer.- Parameters:
piece- the zero-based index of the piece being acted onbegin- the byte offset into the piece where the cancelled block startslength- the size of the cancelled block in bytes
checkDiscardRatio
private void checkDiscardRatio(int length) Count cancelled bytes and ban the peer if it cancels most of what it requests. Conservative: requires at least DISCARD_MIN_BYTES of volume and a cancel ratio above DISCARD_BAN_PERCENT, so legitimate peers under congestion are not affected.- Parameters:
length- the number of cancelled bytes
extensionMessage
void extensionMessage(int id, byte[] bs) Handle an extension message from the peer.- Parameters:
id- the extension message IDbs- the message payload
dontHaveMessage
void dontHaveMessage(int piece) Handle a BEP 54 lt_donthave message: the peer no longer has a piece it advertised. Without the Fast extension, silently cancel outstanding requests for the piece.- Parameters:
piece- the piece index
setMetaInfo
Switch from magnet mode to normal mode. If we already have the metainfo, this does nothing.- Parameters:
meta- non-null
portMessage
void portMessage(int port) Handle a port message from the peer.- Parameters:
port- the port number
suggestMessage
void suggestMessage(int piece) BEP 6 Suggest. Treated as a one-shot request hint: the peer is hinting that this piece is a good one to request from it. Request it immediately if we want it and are unchoked. Never recorded as a permanent have, as that would distort availability for the swarm.- Parameters:
piece- the zero-based index of the piece being suggested
haveMessage
void haveMessage(boolean isAll) BEP 6.- Parameters:
isAll- true for have_all, false for have_none
rejectMessage
void rejectMessage(int piece, int begin, int length) BEP 6.- Parameters:
piece- the zero-based index of the piece being acted onbegin- the byte offset into the piece where the rejected block startslength- the size of the rejected block in bytes
allowedFastMessage
void allowedFastMessage(int piece) Handle an allowed fast message (BEP 6): the peer will serve this piece even while it chokes us. Store it and pull a request now, while we are still choked.Indices outside the accepted derivations (see
expectedAllowedFast()) are dropped, defending against buggy or malicious senders that invent pieces. Membership bounds what such a sender can get us to request while choked to at most the union size.- Parameters:
piece- the piece index
expectedAllowedFast
The set of piece indices a conforming sender may advertise to us, or null when nothing can be derived. The union of two derivations:- our own destination-hash-based set - matches other I2PSnark peers
- the libtorrent-style set derived from a zeroed IPv4 address - qBittorrent and other libtorrent clients over I2P see 0.0.0.0 as their remote address for every I2P connection, so their advertised set is torrent-wide and computable by anyone
Membership is still enforced: indices outside this union are dropped, bounding what a malicious peer can get us to request while choked to at most the union size, and the requests remain subject to normal pipeline limits.
Computed once per connection; called only from the reader thread.
- Returns:
- the expected set, or null for no verification
isAllowedFast
boolean isAllowedFast(int piece) Whether the piece is in our BEP 6 allowed fast set for this peer.- Parameters:
piece- the piece index- Returns:
- true if servable even while we choke the peer
sendAllowedFast
Generate and queue the BEP 6 allowed fast set for this peer.The set is derived from the peer's destination hash, so both ends compute the same pieces. Only pieces we actually have are advertised, and requests for them are served even while we choke the peer.
- Parameters:
ourBitfield- the pieces we have, may be null
generateAllowedFastSet
Generate the BEP 6 allowed fast set for a peer.The first four bytes of the peer's destination hash stand in for the masked IP address, so both ends of an I2P connection can compute the same up to ten piece indices from the torrent infohash.
- Parameters:
peerHash- the 32-byte destination hash of the peerinfohash- the infohash of the torrentpieces- the number of pieces in the torrent- Returns:
- the allowed fast set, never null
unknownMessage
void unknownMessage(int type, byte[] bs) Handle an unknown message type from the peer.- Parameters:
type- the message type bytebs- the message payload
havePiece
void havePiece(int piece) We now have this piece. Tell the peer and cancel any requests for the piece.- Parameters:
piece- the zero-based index of the piece we now hold in full
cancelPiece
void cancelPiece(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 zero-based index of the piece whose requests are dropped
request
private void request(boolean resend) Starts or resumes requesting pieces.- Parameters:
resend- should we resend outstanding requests?
addRequest
void addRequest()Adds a new request to the outstanding requests list. Then send interested if we weren't. Then send new requests if not choked. If nothing to request, send not interested if we were.This is called from several places:
By getOutstandingRequest() when the first part of a chunk comes in By havePiece() when somebody got a new piece completed By chokeMessage() when we receive an unchoke By setInteresting() when we are now interested By PeerCoordinator.updatePiecePriorities()
requestNextPiece
Starts requesting first chunk of next piece. Returns true if something has been added to the requests, false otherwise. Caller should synchronize.- Parameters:
allowed- the pieces servable while choked, or null for no restriction- Returns:
- true if a request was added for this peer, false if nothing was requested
setInteresting
void setInteresting(boolean interest) Whether we are interested in pieces from this peer.- Parameters:
interest- true if interested
setChoking
void setChoking(boolean choke) Whether we are choking the peer.- Parameters:
choke- true to choke, false to unchoke
keepAlive
void keepAlive()Send a keepalive message to the peer.retransmitRequests
void retransmitRequests()Retransmit outstanding requests if necessary. Only call if !supportsFast()getRequests