Example #1
0
 // Called from any client thread.
 public void Send(Item item, CancellationToken token)
 {
     // initialize handle to wait for.
     using (var handle = new ManualResetEventSlim(false))
     {
         var pendingSend = new PendingSend
         {
             ManualResetEvent = handle
         };
         // Make sure the item and pendingSend are put in the same order.
         lock (this.sendLock)
         {
             this.sendQueue.Enqueue(item);
             this.pendingSendQueue.Enqueue(pendingSend);
         }
         // Wait for the just created send handle to notify.
         // May throw operation cancelled, in which case the message is
         // still enqueued... Maybe fix that later.
         handle.Wait(token);
         if (!pendingSend.Success)
         {
             // Now we actually have information why the send
             // failed. Pretty cool.
             throw new CommunicationException(
                       pendingSend.Message,
                       pendingSend.InnerException);
         }
     }
 }
Example #2
0
        public void TrySendMessage(Message message)
        {
            var pendingSend = new PendingSend()
            {
                Message = message
            };

            _sendQueue.Enqueue(pendingSend);
            SendPendingMessages();
        }
Example #3
0
        public Task SendMessageAsync(Message message)
        {
            var tcs         = new TaskCompletionSource <bool>();
            var pendingSend = new PendingSend()
            {
                Message          = message,
                CompletionSource = tcs
            };

            _sendQueue.Enqueue(pendingSend);
            SendPendingMessages();
            return(tcs.Task);
        }
Example #4
0
        private void HandleAck(ushort sequence)
        {
            lock (_sendLock)
            {
                if (_sendSequencer.TryGet(sequence, out PendingOutgoingPacket value))
                {
                    // Notify the user about the ack
                    ChannelRouter.HandlePacketAckedByRemote(connection, channelId, value.NotificationKey);

                    // Dealloc the memory held by the sequencer
                    memoryManager.DeAlloc(value.Memory);

                    // TODO: Remove roundtripping from channeled packets and make specific ping-pong packets

                    // Get the roundtrp
                    ulong roundtrip = (ulong)Math.Round((NetTime.Now - value.FirstSent).TotalMilliseconds);

                    // Report to the connection
                    connection.AddRoundtripSample(roundtrip);

                    // Kill the packet
                    _sendSequencer.Remove(sequence);

                    if (sequence == (ushort)(_outgoingLowestAckedSequence + 1))
                    {
                        // This was the next one.
                        _outgoingLowestAckedSequence++;
                    }
                }

                // Loop from the lowest ack we got
                for (ushort i = _outgoingLowestAckedSequence; !_sendSequencer.Contains(i) && SequencingUtils.Distance(i, _lastOutgoingSequence, sizeof(ushort)) <= 0; i++)
                {
                    _outgoingLowestAckedSequence = i;
                }

                // Check if we can start draining pending pool
                while (_pendingSends.Count > 0 && _sendSequencer.CanSet((ushort)(_lastOutgoingSequence + 1)))
                {
                    // Dequeue the pending
                    PendingSend pending = _pendingSends.Dequeue();

                    // Sequence it
                    CreateOutgoingMessageInternal(new ArraySegment <byte>(pending.Memory.Buffer, (int)pending.Memory.VirtualOffset, (int)pending.Memory.VirtualCount), pending.NoMerge, pending.NotificationKey);

                    // Dealloc
                    memoryManager.DeAlloc(pending.Memory);
                }
            }
        }
Example #5
0
        /// <summary>
        ///     Enqueue a send operation (asynchronously write data to the outgoing stream).
        /// </summary>
        /// <param name="streamIndex">
        ///     The index of the target stream.
        /// </param>
        /// <param name="data">
        ///     The data to write.
        /// </param>
        /// <param name="cancellation">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the operation.
        /// </param>
        /// <returns>
        ///     A <see cref="Task"/> representing the asynchronous operation.
        /// </returns>
        Task EnqueueSend(byte streamIndex, ArraySegment <byte> data, CancellationToken cancellation)
        {
            Log.LogTrace("Enqueuing {DataLength} bytes for sending on stream {StreamIndex}...", data.Count, streamIndex);

            PendingSend pendingSend = new PendingSend(streamIndex, data, cancellation);

            cancellation.Register(
                () => pendingSend.Completion.TrySetCanceled(cancellation)
                );
            _pendingSends.Add(pendingSend);

            Log.LogTrace("Enqueued {DataLength} bytes for sending on stream {StreamIndex}.", data.Count, streamIndex);

            return(pendingSend.Completion.Task);
        }
Example #6
0
        public async ValueTask BindAsync(IPEndPoint endPoint)
        {
            if (!Connected)
            {
                UdpClient = new UdpClient(endPoint);
                _logger.LogInformation($"Starting listening on {endPoint}.");
                //Create a new thread to receive and process data.
                Tasks.Enqueue(Task.Run(async() =>
                {
                    while (true)
                    {
                        var data = await UdpClient.ReceiveAsync();

                        if (!PendingReceive.ContainsKey(data.RemoteEndPoint))
                        {
                            PendingReceive.AddOrUpdate(data.RemoteEndPoint, new ConcurrentQueue <byte[]>(),
                                                       (_, queue) => queue);
                            PendingConnection.Enqueue(data.RemoteEndPoint);
                            HavePendingSend.AddOrUpdate(data.RemoteEndPoint, false, (_, b) => b);
                            PendingSend.AddOrUpdate(data.RemoteEndPoint, new ConcurrentQueue <byte[]>(),
                                                    (_, queue) => queue);
                        }

                        // 0 for normal content
                        if (data.Buffer[0] == 0)
                        {
                            _logger.LogInformation($"Received a standard packet from {data.RemoteEndPoint}.");
                            var pendingReceive = PendingReceive[data.RemoteEndPoint];
                            //Add data to pending receive
                            pendingReceive.Enqueue(data.Buffer[4..]);

                            //Reply received
                            await UdpClient.SendAsync(new byte[] { 2, 0, 0, 0 }, 4, data.RemoteEndPoint);
                        }

                        // 1 for receive feedback
                        if (data.Buffer[0] == 1)
                        {
                            _logger.LogInformation($"Received a confirm packet from {data.RemoteEndPoint}.");

                            //Enqueue confirmed info
                            ConfirmedReceived.Enqueue(data.RemoteEndPoint);
                        }
                    }
Example #7
0
 public virtual void Broadcast(NetBuffer data, NetChannel channel)
 {
     PendingSend msg = new PendingSend();
     msg.message = ProcessOutboundMessage(null, data);
     msg.to = null;
     msg.channel = channel;
     lock (PendingSends)
     {
         PendingSends.Add(msg);
     }
 }
Example #8
0
 public virtual void SendMessage( NetConnection to, NetBuffer message, NetChannel channel )
 {
     PendingSend msg = new PendingSend();
     msg.message = ProcessOutboundMessage(to,message);
     msg.to = to;
     msg.channel = channel;
     lock(PendingSends)
     {
         PendingSends.Add(msg);
     }
 }
Example #9
0
        public void HandleAck(ArraySegment <byte> payload)
        {
            // Read the sequence number
            ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8));
            // Read the raw fragment data
            ushort encodedFragment = (ushort)(payload.Array[payload.Offset + 2] | (ushort)(payload.Array[payload.Offset + 3] << 8));
            // The fragmentId is the last 15 least significant bits
            ushort fragment = (ushort)(encodedFragment & 32767);
            // IsFinal is the most significant bit
            bool isFinal = (ushort)((encodedFragment & 32768) >> 15) == 1;

            lock (_sendLock)
            {
                if (_sendSequencer.TryGet(sequence, out PendingOutgoingPacketFragmented value) && value.Fragments.VirtualCount > fragment && value.Fragments.Pointers[fragment] != null)
                {
                    // Dealloc the memory held by the sequencer for the packet
                    ((PendingOutgoingFragment)value.Fragments.Pointers[fragment]).DeAlloc(memoryManager);

                    // TODO: Remove roundtripping from channeled packets and make specific ping-pong packets

                    // Get the roundtrp
                    ulong roundtrip = (ulong)Math.Round((NetTime.Now - ((PendingOutgoingFragment)value.Fragments.Pointers[fragment]).FirstSent).TotalMilliseconds);

                    // Report to the connection
                    connection.AddRoundtripSample(roundtrip);

                    // Kill the fragment packet
                    value.Fragments.Pointers[fragment] = null;

                    bool hasAllocatedAndAliveFragments = false;
                    for (int i = 0; i < value.Fragments.VirtualCount; i++)
                    {
                        if (value.Fragments.Pointers[i] != null)
                        {
                            hasAllocatedAndAliveFragments = true;
                            break;
                        }
                    }

                    if (!hasAllocatedAndAliveFragments)
                    {
                        // Notify user that the packet was acked
                        ChannelRouter.HandlePacketAckedByRemote(connection, channelId, value.NotificationKey);

                        // Dealloc the wrapper packet
                        value.DeAlloc(memoryManager);

                        // Kill the wrapper packet
                        _sendSequencer.Remove(sequence);

                        if (sequence == (ushort)(_outgoingLowestAckedSequence + 1))
                        {
                            // This was the next one.
                            _outgoingLowestAckedSequence++;
                        }
                    }
                }

                // Loop from the lowest ack we got
                for (ushort i = _outgoingLowestAckedSequence; !_sendSequencer.TryGet(i, out value) && SequencingUtils.Distance(i, _lastOutgoingSequence, sizeof(ushort)) <= 0; i++)
                {
                    _outgoingLowestAckedSequence = i;
                }

                // Check if we can start draining pending pool
                while (_pendingSends.Count > 0 && _sendSequencer.CanSet((ushort)(_lastOutgoingSequence + 1)))
                {
                    // Dequeue the pending
                    PendingSend pending = _pendingSends.Dequeue();

                    // Sequence it
                    CreateOutgoingMessageInternal(new ArraySegment <byte>(pending.Memory.Buffer, (int)pending.Memory.VirtualOffset, (int)pending.Memory.VirtualCount), pending.NoMerge, pending.NotificationKey);

                    // Dealloc
                    memoryManager.DeAlloc(pending.Memory);
                }
            }
        }
Example #10
0
        private async Task SendLoopAsync(SshConnection connection)
        {
            CancellationToken abortToken = _abortCts.Token;

            try
            {
                while (true)
                {
                    PendingSend send = await _sendQueue !.Reader.ReadAsync(abortToken); // TODO: maybe use ReadAllAsync
                    try
                    {
                        // Disable send.CancellationToken.
                        send.CancellationTokenRegistration.Dispose();
                        if (!send.TaskCompletion.Task.IsCanceled)
                        {
                            // If we weren't canceled by send.CancellationToken, do the send.
                            // We use abortToken instead of send.CancellationToken because
                            // we can't allow partial sends unless we're aborting the connection.
                            await connection.SendPacketAsync(send.Packet, abortToken);

                            SemaphoreSlim?keyExchangeSemaphore = null;
                            if (send.Packet.MessageType == MessageNumber.SSH_MSG_KEXINIT)
                            {
                                keyExchangeSemaphore    = _keyReExchangeSemaphore;
                                _keyReExchangeSemaphore = null;
                            }

                            send.TaskCompletion.SetResult(true);

                            // Don't send any more packets until Key Re-Exchange completed.
                            if (keyExchangeSemaphore != null)
                            {
                                await keyExchangeSemaphore.WaitAsync(abortToken);

                                keyExchangeSemaphore.Dispose();
                            }
                        }
                    }
                    catch (Exception e) // SendPacket failed or connection aborted.
                    {
                        Abort(e);

                        // The sender isn't responsible for the fail,
                        // report this as canceled.
                        send.TaskCompletion.SetCanceled();
                    }
                }
            }
            catch (Exception e) // Happens on Abort.
            {
                // Ensure Abort is called so further SendPacketAsync calls return Canceled.
                Abort(e); // In case the Exception was not caused by Abort.
            }
            finally
            {
                // Empty _sendQueue and prevent new sends.
                if (_sendQueue != null)
                {
                    _sendQueue.Writer.Complete();

                    while (_sendQueue.Reader.TryRead(out PendingSend send))
                    {
                        send.CancellationTokenRegistration.Dispose();
                        if (!send.TaskCompletion.Task.IsCanceled)
                        {
                            send.TaskCompletion.SetCanceled();
                        }
                    }
                }
            }
        }