Ejemplo n.º 1
0
        internal async void ReceiveMessagesAsync(IConnection connection, IEncryption decryptor, RateLimiterGroup downloadLimiter, ConnectionMonitor monitor, TorrentManager torrentManager, PeerId id)
        {
            await MainLoop.SwitchToThreadpool();

            ByteBufferPool.Releaser releaser = default;
            try {
                while (true)
                {
                    if (id.AmRequestingPiecesCount == 0 && releaser.Buffer != null)
                    {
                        releaser.Dispose();
                        releaser = NetworkIO.BufferPool.Rent(1, out ByteBuffer _);
                    }
                    else if (id.AmRequestingPiecesCount > 0 && releaser.Buffer == null)
                    {
                        releaser.Dispose();
                        releaser = NetworkIO.BufferPool.Rent(Piece.BlockSize, out ByteBuffer _);
                    }
                    PeerMessage message = await PeerIO.ReceiveMessageAsync(connection, decryptor, downloadLimiter, monitor, torrentManager.Monitor, torrentManager, releaser.Buffer).ConfigureAwait(false);

                    HandleReceivedMessage(id, torrentManager, message);
                }
            } catch {
                releaser.Dispose();
                await ClientEngine.MainLoop;
                CleanupSocket(torrentManager, id);
            }
        }
Ejemplo n.º 2
0
        public static async ReusableTask ReceiveAsync(IConnection connection, ByteBuffer buffer, int offset, int count, IRateLimiter rateLimiter, SpeedMonitor peerMonitor, SpeedMonitor managerMonitor)
        {
            await MainLoop.SwitchToThreadpool();

            while (count > 0)
            {
                int  transferred;
                bool unlimited  = rateLimiter?.Unlimited ?? true;
                int  shouldRead = unlimited ? count : Math.Min(ChunkLength, count);

                if (rateLimiter != null && !unlimited && !rateLimiter.TryProcess(shouldRead))
                {
                    var tcs = new ReusableTaskCompletionSource <int> ();
                    lock (receiveQueue)
                        receiveQueue.Enqueue(new QueuedIO(connection, buffer, offset, shouldRead, rateLimiter, tcs));
                    transferred = await tcs.Task.ConfigureAwait(false);
                }
                else
                {
                    transferred = await connection.ReceiveAsync(buffer, offset, shouldRead).ConfigureAwait(false);
                }

                if (transferred == 0)
                {
                    throw new ConnectionClosedException("Socket receive returned 0, indicating the connection has been closed.");
                }

                peerMonitor?.AddDelta(transferred);
                managerMonitor?.AddDelta(transferred);

                offset += transferred;
                count  -= transferred;
            }
        }
Ejemplo n.º 3
0
        public static async ReusableTask <(PeerMessage message, PeerMessage.Releaser releaser)> ReceiveMessageAsync(IPeerConnection connection, IEncryption decryptor, IRateLimiter?rateLimiter, ConnectionMonitor?peerMonitor, ConnectionMonitor?managerMonitor, ITorrentManagerInfo?torrentData, Memory <byte> buffer)
        {
            await MainLoop.SwitchToThreadpool();

            int messageHeaderLength = 4;
            int messageBodyLength;

            Memory <byte> messageHeaderBuffer = buffer;
            Memory <byte> messageBuffer       = buffer;

            ByteBufferPool.Releaser messageHeaderReleaser = default;
            ByteBufferPool.Releaser messageBufferReleaser = default;

            if (messageHeaderBuffer.IsEmpty)
            {
                messageHeaderReleaser = NetworkIO.BufferPool.Rent(messageHeaderLength, out messageHeaderBuffer);
            }
            using (messageHeaderReleaser) {
                await NetworkIO.ReceiveAsync(connection, messageHeaderBuffer.Slice(0, messageHeaderLength), rateLimiter, peerMonitor?.ProtocolDown, managerMonitor?.ProtocolDown).ConfigureAwait(false);

                decryptor.Decrypt(messageHeaderBuffer.Span.Slice(0, messageHeaderLength));

                messageBodyLength = Message.ReadInt(messageHeaderBuffer.Span);
                if (messageBodyLength < 0 || messageBodyLength > MaxMessageLength)
                {
                    connection.Dispose();
                    throw new ProtocolException($"Invalid message length received. Value was '{messageBodyLength}'");
                }

                if (messageBodyLength == 0)
                {
                    return(KeepAliveMessage.Instance, default);
Ejemplo n.º 4
0
        public static async ReusableTask SendAsync(IPeerConnection connection, SocketMemory buffer, IRateLimiter rateLimiter, SpeedMonitor peerMonitor, SpeedMonitor managerMonitor)
        {
            await MainLoop.SwitchToThreadpool();

            while (buffer.Length > 0)
            {
                int  transferred;
                bool unlimited  = rateLimiter?.Unlimited ?? true;
                int  shouldRead = unlimited ? buffer.Length : Math.Min(ChunkLength, buffer.Length);

                if (rateLimiter != null && !unlimited && !rateLimiter.TryProcess(shouldRead))
                {
                    var tcs = new ReusableTaskCompletionSource <int> ();
                    lock (sendQueue)
                        sendQueue.Enqueue(new QueuedIO(connection, buffer.Slice(0, shouldRead), rateLimiter, tcs));
                    transferred = await tcs.Task.ConfigureAwait(false);
                }
                else
                {
                    transferred = await connection.SendAsync(buffer.Slice(0, shouldRead)).ConfigureAwait(false);
                }

                if (transferred == 0)
                {
                    throw new ConnectionClosedException("Socket send returned 0, indicating the connection has been closed.");
                }

                peerMonitor?.AddDelta(transferred);
                managerMonitor?.AddDelta(transferred);

                buffer = buffer.Slice(transferred);
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer whose message queue you want to start processing</param>
        internal async void TryProcessQueue(TorrentManager manager, PeerId id)
        {
            if (!id.MessageQueue.BeginProcessing())
            {
                return;
            }

            await MainLoop.SwitchToThreadpool();

            ByteBufferPool.Releaser socketMemoryReleaser = default;
            Memory <byte>           socketMemory         = default;

            try {
                while (id.MessageQueue.TryDequeue(out PeerMessage? msg, out PeerMessage.Releaser msgReleaser))
                {
                    using var autorelease = msgReleaser;

                    if (socketMemory.IsEmpty || socketMemory.Length < msg.ByteLength)
                    {
                        socketMemoryReleaser.Dispose();
                        socketMemoryReleaser = NetworkIO.BufferPool.Rent(msg.ByteLength, out socketMemory);
                    }

                    var buffer = socketMemory.Slice(0, msg.ByteLength);
                    if (msg is PieceMessage pm)
                    {
                        pm.SetData((default, buffer.Slice(buffer.Length - pm.RequestLength)));
Ejemplo n.º 6
0
        internal async void ReceiveMessagesAsync(IPeerConnection connection, IEncryption decryptor, RateLimiterGroup downloadLimiter, ConnectionMonitor monitor, TorrentManager torrentManager, PeerId id)
        {
            await MainLoop.SwitchToThreadpool();

            Memory <byte> currentBuffer = default;

            Memory <byte> smallBuffer = default;

            ByteBufferPool.Releaser smallReleaser = default;

            Memory <byte> largeBuffer = default;

            ByteBufferPool.Releaser largeReleaser = default;
            try {
                while (true)
                {
                    if (id.AmRequestingPiecesCount == 0)
                    {
                        if (!largeBuffer.IsEmpty)
                        {
                            largeReleaser.Dispose();
                            largeReleaser = default;
                            largeBuffer   = currentBuffer = default;
                        }
                        if (smallBuffer.IsEmpty)
                        {
                            smallReleaser = NetworkIO.BufferPool.Rent(ByteBufferPool.SmallMessageBufferSize, out smallBuffer);
                            currentBuffer = smallBuffer;
                        }
                    }
                    else
                    {
                        if (!smallBuffer.IsEmpty)
                        {
                            smallReleaser.Dispose();
                            smallReleaser = default;
                            smallBuffer   = currentBuffer = default;
                        }
                        if (largeBuffer.IsEmpty)
                        {
                            largeReleaser = NetworkIO.BufferPool.Rent(ByteBufferPool.LargeMessageBufferSize, out largeBuffer);
                            currentBuffer = largeBuffer;
                        }
                    }

                    (PeerMessage message, PeerMessage.Releaser releaser) = await PeerIO.ReceiveMessageAsync(connection, decryptor, downloadLimiter, monitor, torrentManager.Monitor, torrentManager, currentBuffer).ConfigureAwait(false);

                    HandleReceivedMessage(id, torrentManager, message, releaser);
                }
            } catch {
                await ClientEngine.MainLoop;
                CleanupSocket(torrentManager, id);
            } finally {
                smallReleaser.Dispose();
                largeReleaser.Dispose();
            }
        }
Ejemplo n.º 7
0
        public static async ReusableTask <PeerMessage> ReceiveMessageAsync(IConnection connection, IEncryption decryptor, IRateLimiter rateLimiter, ConnectionMonitor peerMonitor, ConnectionMonitor managerMonitor, ITorrentData torrentData, ByteBuffer buffer)
        {
            await MainLoop.SwitchToThreadpool();

            int messageHeaderLength = 4;
            int messageBodyLength;

            ByteBuffer messageHeaderBuffer = buffer;
            ByteBuffer messageBuffer       = buffer;

            ByteBufferPool.Releaser messageBufferReleaser = default;

            using (var headerReleaser = buffer == null ? NetworkIO.BufferPool.Rent(messageHeaderLength, out messageHeaderBuffer) : default) {
                await NetworkIO.ReceiveAsync(connection, messageHeaderBuffer, 0, messageHeaderLength, rateLimiter, peerMonitor?.ProtocolDown, managerMonitor?.ProtocolDown).ConfigureAwait(false);

                decryptor.Decrypt(messageHeaderBuffer.Data, 0, messageHeaderLength);

                messageBodyLength = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(messageHeaderBuffer.Data, 0));
                if (messageBodyLength < 0 || messageBodyLength > MaxMessageLength)
                {
                    connection.Dispose();
                    throw new ProtocolException($"Invalid message length received. Value was '{messageBodyLength}'");
                }

                if (messageBodyLength == 0)
                {
                    return(new KeepAliveMessage());
                }

                if (buffer == null || buffer.Data.Length < messageBodyLength + messageHeaderLength)
                {
                    messageBufferReleaser = NetworkIO.BufferPool.Rent(messageBodyLength + messageHeaderLength, out messageBuffer);
                    Buffer.BlockCopy(messageHeaderBuffer.Data, 0, messageBuffer.Data, 0, messageHeaderLength);
                }
            }

            using (messageBufferReleaser) {
                // Always assume protocol first, then convert to data when we what message it is!
                await NetworkIO.ReceiveAsync(connection, messageBuffer, messageHeaderLength, messageBodyLength, rateLimiter, peerMonitor?.ProtocolDown, managerMonitor?.ProtocolDown).ConfigureAwait(false);

                decryptor.Decrypt(messageBuffer.Data, messageHeaderLength, messageBodyLength);
                // FIXME: manager should never be null, except some of the unit tests do that.
                var data = PeerMessage.DecodeMessage(messageBuffer.Data, 0, messageHeaderLength + messageBodyLength, torrentData);
                if (data is PieceMessage msg)
                {
                    peerMonitor?.ProtocolDown.AddDelta(-msg.RequestLength);
                    managerMonitor?.ProtocolDown.AddDelta(-msg.RequestLength);

                    peerMonitor?.DataDown.AddDelta(msg.RequestLength);
                    managerMonitor?.DataDown.AddDelta(msg.RequestLength);
                }
                return(data);
            }
        }
Ejemplo n.º 8
0
        public static async ReusableTask <HandshakeMessage> ReceiveHandshakeAsync(IPeerConnection connection, IEncryption decryptor)
        {
            await MainLoop.SwitchToThreadpool();

            using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out Memory <byte> buffer)) {
                await NetworkIO.ReceiveAsync(connection, buffer, null, null, null).ConfigureAwait(false);

                decryptor.Decrypt(buffer.Span);

                return(new HandshakeMessage(buffer.Span));
            }
        }
Ejemplo n.º 9
0
        public static async ReusableTask <HandshakeMessage> ReceiveHandshakeAsync(IConnection connection, IEncryption decryptor)
        {
            await MainLoop.SwitchToThreadpool();

            using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out ByteBuffer buffer)) {
                await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                decryptor.Decrypt(buffer.Data, 0, HandshakeMessage.HandshakeLength);

                var message = new HandshakeMessage();
                message.Decode(buffer.Data, 0, HandshakeMessage.HandshakeLength);
                return(message);
            }
        }
Ejemplo n.º 10
0
        async void ProcessQueue()
        {
            // Ensure this doesn't run on the UI thread as the networking calls can do some (partially) blocking operations.
            // Specifically 'NetworkInterface.GetAllNetworkInterfaces' is synchronous and can take hundreds of milliseconds.
            await MainLoop.SwitchToThreadpool();

            await RateLimiterTask;

            using var sendingClient = new UdpClient();
            var nics = NetworkInterface.GetAllNetworkInterfaces();

            while (true)
            {
                InfoHash infoHash = null;
                lock (PendingAnnounces) {
                    if (PendingAnnounces.Count == 0)
                    {
                        // Enforce a minimum delay before the next announce to avoid killing CPU by iterating network interfaces.
                        RateLimiterTask     = Task.Delay(1000);
                        ProcessingAnnounces = false;
                        break;
                    }
                    infoHash = PendingAnnounces.Dequeue();
                }

                string message = string.Format(BaseSearchString, ListenPort, infoHash.ToHex());
                byte[] data    = Encoding.ASCII.GetBytes(message);

                foreach (var nic in nics)
                {
                    try {
                        sendingClient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, IPAddress.HostToNetworkOrder(nic.GetIPProperties().GetIPv4Properties().Index));
                        await sendingClient.SendAsync(data, data.Length, MulticastAddressV4).ConfigureAwait(false);
                    } catch {
                        // If data can't be sent, just ignore the error
                    }
                }
            }
        }
Ejemplo n.º 11
0
        public static async ReusableTask SendMessageAsync(IConnection connection, IEncryption encryptor, PeerMessage message, IRateLimiter rateLimiter, ConnectionMonitor peerMonitor, ConnectionMonitor managerMonitor, ByteBuffer buffer = null)
        {
            await MainLoop.SwitchToThreadpool();

            int count = message.ByteLength;

            using (buffer == null ? NetworkIO.BufferPool.Rent(count, out buffer) : default) {
                var pieceMessage = message as PieceMessage;
                message.Encode(buffer.Data, 0);
                encryptor.Encrypt(buffer.Data, 0, count);

                // Assume protocol first, then swap it to data once we successfully send the data bytes.
                await NetworkIO.SendAsync(connection, buffer, 0, count, pieceMessage == null?null : rateLimiter, peerMonitor?.ProtocolUp, managerMonitor?.ProtocolUp).ConfigureAwait(false);

                if (pieceMessage != null)
                {
                    peerMonitor?.ProtocolUp.AddDelta(-pieceMessage.RequestLength);
                    managerMonitor?.ProtocolUp.AddDelta(-pieceMessage.RequestLength);

                    peerMonitor?.DataUp.AddDelta(pieceMessage.RequestLength);
                    managerMonitor?.DataUp.AddDelta(pieceMessage.RequestLength);
                }
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer whose message queue you want to start processing</param>
        internal async void TryProcessQueue(TorrentManager manager, PeerId id)
        {
            if (!id.MessageQueue.BeginProcessing())
            {
                return;
            }

            await MainLoop.SwitchToThreadpool();

            ByteBufferPool.Releaser messageBuffer = default;
            ByteBufferPool.Releaser pieceBuffer   = default;
            PeerMessage             msg;

            try {
                while ((msg = id.MessageQueue.TryDequeue()) != null)
                {
                    var msgLength = msg.ByteLength;

                    if (msg is PieceMessage pm)
                    {
                        if (pieceBuffer.Buffer == null)
                        {
                            pieceBuffer = DiskManager.BufferPool.Rent(msgLength, out ByteBuffer _);
                        }
                        pm.DataReleaser = pieceBuffer;
                        try {
                            await DiskManager.ReadAsync(manager, pm.StartOffset + ((long)pm.PieceIndex * manager.Torrent.PieceLength), pm.Data, pm.RequestLength).ConfigureAwait(false);
                        } catch (Exception ex) {
                            await ClientEngine.MainLoop;
                            manager.TrySetError(Reason.ReadFailure, ex);
                            return;
                        }
                        System.Threading.Interlocked.Increment(ref id.piecesSent);
                    }
                    else
                    {
                        pieceBuffer.Dispose();
                    }

                    if (messageBuffer.Buffer == null || messageBuffer.Buffer.Data.Length < msg.ByteLength)
                    {
                        messageBuffer.Dispose();
                        messageBuffer = NetworkIO.BufferPool.Rent(msgLength, out ByteBuffer _);
                    }
                    await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, msg, manager.UploadLimiters, id.Monitor, manager.Monitor, messageBuffer.Buffer).ConfigureAwait(false);

                    if (msg is PieceMessage)
                    {
                        System.Threading.Interlocked.Decrement(ref id.isRequestingPiecesCount);
                    }

                    id.LastMessageSent.Restart();
                }
            } catch {
                await ClientEngine.MainLoop;
                CleanupSocket(manager, id);
            } finally {
                messageBuffer.Dispose();
                pieceBuffer.Dispose();
            }
        }
Ejemplo n.º 13
0
        public static async ReusableTask ConnectAsync(IPeerConnection connection)
        {
            await MainLoop.SwitchToThreadpool();

            await connection.ConnectAsync();
        }