// 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); } } }
public void TrySendMessage(Message message) { var pendingSend = new PendingSend() { Message = message }; _sendQueue.Enqueue(pendingSend); SendPendingMessages(); }
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); }
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); } } }
/// <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); }
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); } }
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); } }
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); } }
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); } } }
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(); } } } } }