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 void CreateOutgoingMessageInternal(ArraySegment <byte> payload, bool noMerge, ulong notificationKey) { if (payload.Count > connection.MTU) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("Tried to send message that was too large. Use a fragmented channel instead. [Size=" + payload.Count + "] [MaxMessageSize=" + config.MaxFragments + "]"); } return; } if (!_sendSequencer.CanSet((ushort)(_lastOutgoingSequence + 1))) { if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Outgoing packet window is exhausted. Expect delays"); } // Alloc memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 0, payload.Count); // Enqueue it _pendingSends.Enqueue(new PendingSend() { Memory = memory, NoMerge = noMerge, NotificationKey = notificationKey }); } else { // Increment the sequence number _lastOutgoingSequence++; // Allocate the memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count + 4); // Write headers memory.Buffer[0] = HeaderPacker.Pack(MessageType.Data); memory.Buffer[1] = channelId; // Write the sequence memory.Buffer[2] = (byte)_lastOutgoingSequence; memory.Buffer[3] = (byte)(_lastOutgoingSequence >> 8); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 4, payload.Count); // Add the memory to pending _sendSequencer.Set(_lastOutgoingSequence, new PendingOutgoingPacket() { Attempts = 1, LastSent = NetTime.Now, FirstSent = NetTime.Now, Memory = memory, NotificationKey = notificationKey }); // Allocate pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Point the first pointer to the memory pointers.Pointers[0] = memory; // Send the message to the router. Tell the router to NOT dealloc the memory as the channel needs it for resend purposes. ChannelRouter.SendMessage(pointers, false, connection, noMerge, memoryManager); } }
private void CreateOutgoingMessageInternal(ArraySegment <byte> payload, bool noMerge, ulong notificationKey) { // Calculate the amount of fragments required int fragmentsRequired = (payload.Count + (connection.MTU - 1)) / connection.MTU; if (fragmentsRequired >= config.MaxFragments) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("Tried to create message that was too large. [Size=" + payload.Count + "] [FragmentsRequired=" + fragmentsRequired + "] [Config.MaxFragments=" + config.MaxFragments + "]"); } return; } if (!_sendSequencer.CanSet((ushort)(_lastOutgoingSequence + 1))) { if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Outgoing packet window is exhausted. Expect delays"); } // Alloc memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 0, payload.Count); // Enqueue it _pendingSends.Enqueue(new PendingSend() { Memory = memory, NoMerge = noMerge, NotificationKey = notificationKey }); } else { // Increment the sequence number _lastOutgoingSequence++; // Alloc array HeapPointers memoryParts = memoryManager.AllocHeapPointers((uint)fragmentsRequired); int position = 0; for (ushort i = 0; i < fragmentsRequired; i++) { // Calculate message size int messageSize = Math.Min(connection.MTU, payload.Count - position); // Allocate memory for each fragment memoryParts.Pointers[memoryParts.VirtualOffset + i] = memoryManager.AllocHeapMemory((uint)(messageSize + 6)); // Write headers ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[0] = HeaderPacker.Pack(MessageType.Data); ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[1] = channelId; // Write the sequence ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[2] = (byte)_lastOutgoingSequence; ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[3] = (byte)(_lastOutgoingSequence >> 8); // Write the fragment ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[4] = (byte)(i & 32767); ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[5] = (byte)(((i & 32767) >> 8) | (byte)(i == fragmentsRequired - 1 ? 128 : 0)); // Write the payload Buffer.BlockCopy(payload.Array, payload.Offset + position, ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer, 6, messageSize); // Increase the position position += messageSize; } // Alloc outgoing fragment structs HeapPointers outgoingFragments = memoryManager.AllocHeapPointers((uint)fragmentsRequired); for (int i = 0; i < fragmentsRequired; i++) { // Add the memory to the outgoing sequencer array outgoingFragments.Pointers[outgoingFragments.VirtualOffset + i] = new PendingOutgoingFragment() { Attempts = 1, LastSent = NetTime.Now, FirstSent = NetTime.Now, Memory = ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]) }; } // Add the memory to the outgoing sequencer _sendSequencer.Set(_lastOutgoingSequence, new PendingOutgoingPacketFragmented() { Fragments = outgoingFragments, NotificationKey = notificationKey }); // Send the message to the router. Tell the router to NOT dealloc the memory as the channel needs it for resend purposes. ChannelRouter.SendMessage(memoryParts, false, connection, noMerge, memoryManager); } }