/// <summary> /// Creates "request" or "cancel" messages /// </summary> /// <param name="index">Zero-based piece index</param> /// <param name="begin">Zero-based offset within the piece</param> /// <param name="length">Specifies the requested (cancelled) block's length</param> public PeerMessage(PeerMessageType type, int index, int begin, int length) { if (!(type == PeerMessageType.request || type == PeerMessageType.cancel)) { throw new ArgumentException("Type of the message must be \"request\" or \"cancel\""); } messageType = type; msgContents = new byte[msgLenSpace + msgTypeSpace + msgIntSpace * 3]; Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(13)), msgContents, msgLenSpace); msgContents[msgLenSpace] = (byte)messageType; Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(index)), 0, msgContents, msgLenSpace + msgTypeSpace, msgIntSpace); Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(begin)), 0, msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, msgIntSpace); Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(length)), 0, msgContents, msgLenSpace + msgTypeSpace + msgIntSpace * 2, msgIntSpace); this.pieceIndex = index; this.pieceOffset = begin; this.length = length; }
/// <summary> /// Creates a message with no payload (keep-alive, choke, unchoke, interested or not interested) /// </summary> /// <param name="type">Type of the message</param> /// <exception cref="ArgumentException">Thrown if passed message type must have a payload</exception> public PeerMessage(PeerMessageType type) { if (type == PeerMessageType.keepAlive) { msgContents = new byte[msgLenSpace]; } else if (type == PeerMessageType.choke || type == PeerMessageType.unchoke || type == PeerMessageType.interested || type == PeerMessageType.notInterested) { msgContents = new byte[msgLenSpace + msgTypeSpace]; Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(1)), msgContents, msgLenSpace); msgContents[msgLenSpace] = (byte)type; } else { throw new ArgumentException($"This message type: {type.ToString()} must have non-empty payload"); } }
public void ReceivedChokeOrDisconnected(PeerConnection connection) { if (connection.outgoingRequestsCount != 0) { for (int i = 0; i < connection.outgoingRequests.Length; i++) { if (connection.outgoingRequests[i] != null) { // null checking because the list may be already empty lock (pendingIncomingPiecesInfo) { var node = FindRequestsPiece(connection.outgoingRequests[i].Item1); if (node != null) { node.requestedBlocksMap[connection.outgoingRequests[i].Item2 / blockSize] = false; } connection.outgoingRequests[i] = null; } } } } }
/// <summary> /// Creates a bitfield message /// </summary> /// <param name="bitfield">Bitfield to include in the message</param> public PeerMessage(BitArray bitfield) { messageType = PeerMessageType.bitfield; msgContents = new byte[msgLenSpace + msgTypeSpace + (int)Math.Ceiling((double)bitfield.Count / 8)]; rawBytesOffset = msgLenSpace + msgTypeSpace; Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(msgContents.Length - msgLenSpace)), msgContents, msgLenSpace); msgContents[msgLenSpace] = (byte)messageType; int j = 0; for (int i = rawBytesOffset; i < msgContents.Length; i++) { for (int bit = 7; bit >= 0; bit--) { if (j >= bitfield.Count) { break; } msgContents[i] |= (byte)(Convert.ToByte(bitfield[j]) << bit); j++; } } }
// returns piece index, piece offset, block size public Tuple <int, int, int> FindNextRequest(PeerConnection connection) { Tuple <int, int, int> result; // here we search for any non-downloaded and non-requested blocks within pending pieces lock (pendingIncomingPiecesInfo) { foreach (var entry in pendingIncomingPiecesInfo) { // if peer doesn't have this piece, go further if (connection.peersPieces[entry.pieceIndex] == false) { continue; } // otherwise check if we have something left to download in this piece int blockIndex; bool spaceFound = false; for (blockIndex = 0; blockIndex < entry.blocksMap.Count; blockIndex++) { if (entry.blocksMap[blockIndex] == false && entry.requestedBlocksMap[blockIndex] == false) { spaceFound = true; break; } } if (spaceFound) { int resultOffset = blockIndex * blockSize; if (blockIndex == entry.blocksMap.Count - 1) { result = new Tuple <int, int, int>(entry.pieceIndex, resultOffset, GetLastBlockSize(entry.pieceIndex)); } else { result = new Tuple <int, int, int>(entry.pieceIndex, resultOffset, blockSize); } entry.requestedBlocksMap[blockIndex] = true; return(result); } } // if we didn't find anything in pieces we're already downloading (or the Peer doesn't have it), // we simply find the first fitting piece the Peer has int interestingPieceIndex = -1; lock (pieces) { for (int i = 0; i < connection.peersPieces.Count; i++) { if (FindRequestsPiece(i) == null && pieces[i] == false && (connection.peersPieces[i] == true)) { interestingPieceIndex = i; break; } } } // still haven't found anything? Well, this Peer has nothing interesting if (interestingPieceIndex == -1) { return(null); } // peer has some piece we haven't added yet; so we add it and request block with 0-offset (the first one) var newEntry = AddPendingIncomingPiece(interestingPieceIndex); newEntry.requestedBlocksMap[0] = true; pendingIncomingPiecesInfo.AddLast(newEntry); // just for safety (what if it's only one block in piece?) if (0 == newEntry.blocksMap.Count - 1) { return(new Tuple <int, int, int>(interestingPieceIndex, 0, GetLastBlockSize(newEntry.pieceIndex))); } else { return(new Tuple <int, int, int>(interestingPieceIndex, 0, blockSize)); } } }
public PeerMessage(byte[] msgContents, int expectedBitfieldLength) { if (msgContents.Length == msgLenSpace) { messageType = PeerMessageType.keepAlive; return; } if (msgContents[msgLenSpace] == 5) { int bitsCount = (msgContents.Length - msgLenSpace - msgTypeSpace) * 8; if (bitsCount < expectedBitfieldLength || bitsCount > expectedBitfieldLength + 7) { messageType = PeerMessageType.invalid; } if (bitsCount != expectedBitfieldLength) { byte lastByte = msgContents[msgContents.Length - 1]; lastByte <<= bitsCount - expectedBitfieldLength; if (lastByte != 0) { messageType = PeerMessageType.invalid; return; } } } if (msgContents[msgLenSpace] <= 10) { messageType = (PeerMessageType)msgContents[msgLenSpace]; } else { messageType = PeerMessageType.unknown; return; } this.msgContents = msgContents; switch (messageType) { case PeerMessageType.have: byte[] num = new byte[msgIntSpace]; Array.Copy(msgContents, msgLenSpace + msgTypeSpace, num, 0, msgIntSpace); this.pieceIndex = BitConverter.ToInt32(PeerConnection.HTONNTOH(num), 0); break; case PeerMessageType.bitfield: this.rawBytesOffset = msgLenSpace + msgTypeSpace; break; case PeerMessageType.request: case PeerMessageType.cancel: byte[] index = new byte[msgIntSpace]; byte[] begin = new byte[msgIntSpace]; byte[] length = new byte[msgIntSpace]; Array.Copy(msgContents, msgLenSpace + msgTypeSpace, index, 0, msgIntSpace); Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, begin, 0, msgIntSpace); Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace * 2, length, 0, msgIntSpace); this.pieceIndex = BitConverter.ToInt32(PeerConnection.HTONNTOH(index), 0); this.pieceOffset = BitConverter.ToInt32(PeerConnection.HTONNTOH(begin), 0); this.length = BitConverter.ToInt32(PeerConnection.HTONNTOH(length), 0); break; case PeerMessageType.piece: byte[] index1 = new byte[msgIntSpace]; byte[] begin1 = new byte[msgIntSpace]; Array.Copy(msgContents, msgLenSpace + msgTypeSpace, index1, 0, msgIntSpace); Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, begin1, 0, msgIntSpace); this.pieceIndex = BitConverter.ToInt32(PeerConnection.HTONNTOH(index1), 0); this.pieceOffset = BitConverter.ToInt32(PeerConnection.HTONNTOH(begin1), 0); this.rawBytesOffset = msgLenSpace + msgTypeSpace + msgIntSpace * 2; break; case PeerMessageType.port: break; } }