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); } } }
public HeapPointers HandleIncomingMessagePoll(ArraySegment <byte> payload) { // Read the sequence number ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8)); lock (_receiveLock) { if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0 || _receiveSequencer.Contains(sequence)) { // We have already acked this message. Ack again SendAck(sequence); return(null); } else if (sequence == (ushort)(_incomingLowestAckedSequence + 1)) { // This is the packet right after _incomingLowestAckedSequence++; // Send ack SendAck(sequence); uint additionalPackets = 0; // Count all the additional sequential packets that are ready for (int i = 1; (_receiveSequencer.TryGet((ushort)(_incomingLowestAckedSequence + i), out PendingIncomingPacket value)); i++) { additionalPackets++; } // Allocate pointers (alloc size 1, we might need more) HeapPointers pointers = memoryManager.AllocHeapPointers(1 + additionalPackets); // Point the first pointer to the memory that is known. pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); for (int i = 0; _receiveSequencer.TryGet((ushort)(_incomingLowestAckedSequence + 1), out PendingIncomingPacket value); i++) { // Update lowest incoming ++_incomingLowestAckedSequence; // Set the memory pointers.Pointers[1 + i] = memoryManager.AllocMemoryWrapper(value.Memory); // Kill _receiveSequencer.Remove(_incomingLowestAckedSequence); } return(pointers); } else if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) > 0) { // Future packet if (!_receiveSequencer.CanUpdateOrSet(sequence)) { // If we cant update or set, that means the window is full and we are not in the window. if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Incoming packet window is exhausted. Expect delays"); } return(null); } if (!_receiveSequencer.Contains(sequence)) { // Alloc payload plus header memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count - 2); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset + 2, memory.Buffer, 0, payload.Count - 2); // Add to sequencer _receiveSequencer.Set(sequence, new PendingIncomingPacket() { Memory = memory }); // Send ack SendAck(sequence); } } return(null); } }