예제 #1
0
 public QueuedIO(IPeerConnection connection, SocketMemory buffer, IRateLimiter rateLimiter, ReusableTaskCompletionSource <int> tcs)
 {
     this.connection  = connection;
     this.buffer      = buffer;
     this.rateLimiter = rateLimiter;
     this.tcs         = tcs;
 }
예제 #2
0
        public new Releaser Rent(int capacity, out SocketMemory memory)
        {
            var releaser = base.Rent(capacity, out memory);

            memory = memory.Slice(0, capacity);
            return(releaser);
        }
예제 #3
0
        /// <summary>
        /// Where possible we will use a SocketAsyncEventArgs object which has already had
        /// 'SetBuffer(byte[],int,int)' invoked on it for the given byte[]. Reusing these is
        /// much more efficient than constantly calling SetBuffer on a different 'SocketAsyncEventArgs'
        /// object.
        /// </summary>
        /// <param name="buffer">The buffer we wish to get the reusuable 'SocketAsyncEventArgs' for</param>
        /// <returns></returns>
        static SocketAsyncEventArgs GetSocketAsyncEventArgs(SocketMemory buffer)
        {
#if NETSTANDARD2_0
            if (buffer.SocketArgs.Buffer == null)
            {
                buffer.SocketArgs.Completed += Handler;
            }

            if (!MemoryMarshal.TryGetArray(buffer.Memory, out ArraySegment <byte> segment))
            {
                throw new ArgumentException("Could not retrieve the underlying buffer");
            }

            if (buffer.SocketArgs.Buffer == null)
            {
                buffer.SocketArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
            }
            else
            {
                buffer.SocketArgs.SetBuffer(segment.Offset, segment.Count);
            }
#else
            if (buffer.SocketArgs.MemoryBuffer.IsEmpty)
            {
                buffer.SocketArgs.Completed += Handler;
            }
            buffer.SocketArgs.SetBuffer(buffer.Memory);
#endif
            return(buffer.SocketArgs);
        }
예제 #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);
            }
        }
예제 #5
0
        public static async ReusableTask <(PeerMessage message, PeerMessage.Releaser releaser)> ReceiveMessageAsync(IPeerConnection connection, IEncryption decryptor, IRateLimiter rateLimiter, ConnectionMonitor peerMonitor, ConnectionMonitor managerMonitor, ITorrentData torrentData, SocketMemory buffer)
        {
            await MainLoop.SwitchToThreadpool();

            int messageHeaderLength = 4;
            int messageBodyLength;

            SocketMemory messageHeaderBuffer = buffer;
            SocketMemory 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.AsSpan(0, messageHeaderLength));

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

                if (messageBodyLength == 0)
                {
                    return(KeepAliveMessage.Instance, default);
예제 #6
0
        public ReusableTask <int> SendAsync(SocketMemory buffer)
        {
            SocketAsyncEventArgs args = GetSocketAsyncEventArgs(buffer);

            args.UserToken = SendTcs;

            AsyncFlowControl?control = null;

            if (!ExecutionContext.IsFlowSuppressed())
            {
                control = ExecutionContext.SuppressFlow();
            }

            try {
                if (!Socket.SendAsync(args))
                {
                    SendTcs.SetResult(buffer.Length);
                }
            } catch (ObjectDisposedException) {
                SendTcs.SetResult(0);
            } finally {
                control?.Undo();
            }

            return(SendTcs.Task);
        }
예제 #7
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;
            SocketMemory            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, socketMemory) = NetworkIO.BufferPool.Rent(msg.ByteLength);
                    }

                    var buffer = socketMemory.Slice(0, msg.ByteLength);
                    if (msg is PieceMessage pm)
                    {
                        pm.SetData((default, buffer.Memory.Slice(buffer.Length - pm.RequestLength)));
예제 #8
0
        protected async ReusableTask ReceiveMessageAsync(SocketMemory buffer)
        {
            if (buffer.Length == 0)
            {
                return;
            }
            if (!initialBuffer.IsEmpty)
            {
                int toCopy = Math.Min(initialBuffer.Length, buffer.Length);
                initialBuffer.Span.Slice(0, toCopy).CopyTo(buffer.AsSpan());
                initialBuffer = initialBuffer.Slice(toCopy);

                if (toCopy != buffer.Length)
                {
                    await NetworkIO.ReceiveAsync(socket, buffer.Slice(toCopy, buffer.Length - toCopy), null, null, null).ConfigureAwait(false);

                    bytesReceived += buffer.Length - toCopy;
                }
            }
            else
            {
                await NetworkIO.ReceiveAsync(socket, buffer, null, null, null).ConfigureAwait(false);

                bytesReceived += buffer.Length;
            }
        }
예제 #9
0
        internal async void ReceiveMessagesAsync(IPeerConnection connection, IEncryption decryptor, RateLimiterGroup downloadLimiter, ConnectionMonitor monitor, TorrentManager torrentManager, PeerId id)
        {
            await MainLoop.SwitchToThreadpool();

            SocketMemory currentBuffer = default;

            SocketMemory smallBuffer = default;

            ByteBufferPool.Releaser smallReleaser = default;

            SocketMemory 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();
            }
        }
예제 #10
0
        private async Task CompleteSendOrReceiveFirst(SocketMemory buffer)
        {
            var allRequests    = new List <RequestMessage> ();
            var requestsBuffer = requests.Encode().AsMemory();

            while (requestsBuffer.Length > 0)
            {
                var message = (RequestMessage)PeerMessage.DecodeMessage(requestsBuffer.Span, null).message;
                allRequests.Add(message);
                requestsBuffer = requestsBuffer.Slice(message.ByteLength);
            }

            while (allRequests.Count > 0)
            {
                int size = Message.ReadInt(buffer.Span);

                await NetworkIO.ReceiveAsync(connection, buffer.Slice(4, size), null, null, null);

                PieceMessage m       = (PieceMessage)PeerMessage.DecodeMessage(buffer.AsSpan(0, size + 4), rig.Manager).message;
                var          request = allRequests[0];
                Assert.AreEqual(request.PieceIndex, m.PieceIndex, "#1");
                Assert.AreEqual(request.RequestLength, m.RequestLength, "#1");
                Assert.AreEqual(request.StartOffset, m.StartOffset, "#1");

                for (int i = 0; i < request.RequestLength; i++)
                {
                    if (buffer.Span[i + 13] != (byte)(m.PieceIndex * rig.Torrent.PieceLength + m.StartOffset + i))
                    {
                        throw new Exception("Corrupted data received");
                    }
                }

                allRequests.RemoveAt(0);

                if (allRequests.Count == 0)
                {
                    break;
                }
                else
                {
                    await NetworkIO.ReceiveAsync(connection, buffer.Slice(0, 4), null, null, null);;
                }
            }

            Uri baseUri = new Uri(ListenerURL);

            baseUri = new Uri(baseUri, $"{rig.Manager.Torrent.Name}/");
            if (rig.Manager.Torrent.Files.Count > 1)
            {
                Assert.AreEqual(new Uri(baseUri, rig.Manager.Torrent.Files[0].Path), requestedUrl[0]);
                Assert.AreEqual(new Uri(baseUri, rig.Manager.Torrent.Files[1].Path), requestedUrl[1]);
            }
        }
예제 #11
0
        public async ReusableTask <int> ReceiveAsync(SocketMemory buffer)
        {
            if (SlowConnection)
            {
                buffer = buffer.Slice(0, Math.Min(88, buffer.Length));
            }

            var result = await ReadStream.ReadAsync(buffer.Memory);

            Receives.Add(result);
            return(ManualBytesReceived ?? result);
        }
예제 #12
0
        public async ReusableTask <int> SendAsync(SocketMemory buffer)
        {
            if (SlowConnection)
            {
                buffer = buffer.Slice(0, Math.Min(88, buffer.Length));
            }

            var data = buffer.Memory.ToArray();
            await WriteStream.WriteAsync(data, 0, data.Length, CancellationToken.None);

            Sends.Add(buffer.Length);
            return(ManualBytesSent ?? buffer.Length);
        }
예제 #13
0
        async Task Send(SocketMemory buffer, int maxBytesPerChunk = -1)
        {
            if (maxBytesPerChunk == -1)
            {
                await NetworkIO.SendAsync(connection, buffer, null, null, null);
            }
            else
            {
                while (buffer.Length > 0)
                {
                    var toSend = Math.Min(maxBytesPerChunk, buffer.Length);
                    await NetworkIO.SendAsync(connection, buffer.Slice(0, toSend), null, null, null);

                    buffer = buffer.Slice(toSend);
                }
            }
        }
예제 #14
0
 public static ReusableTask SendAsync(IPeerConnection connection, SocketMemory buffer)
 {
     return(SendAsync(connection, buffer, null, null, null));
 }
예제 #15
0
 public ReusableTask <int> SendAsync(SocketMemory buffer)
 {
     return(ReusableTask.FromResult(0));
 }