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) { // Set the new sequence _incomingLowestAckedSequence = sequence; // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } /* else * Logging.LogInfo("Dropped packet in UnreliableOrdered channel..."); */ return(null); } }
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 (_incomingAckedPackets.Contains(sequence)) { // We have already received this message. Ignore it. return(null); } // Add to sequencer _incomingAckedPackets.Set(sequence, true); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } }
internal static void SendMessage(ArraySegment <byte> payload, Connection connection, byte channelId, bool noDelay, MemoryManager memoryManager) { if (channelId < 0 || channelId >= connection.Channels.Length) { throw new ArgumentOutOfRangeException(nameof(channelId), channelId, "ChannelId was out of range"); } IChannel channel = connection.Channels[channelId]; lock (_memoryPointerLock) { HeapPointers memoryPointers = channel.CreateOutgoingMessage(payload, out byte headerSize, out bool dealloc); if (memoryPointers != null) { for (int i = 0; i < memoryPointers.VirtualCount; i++) { connection.SendRaw(new ArraySegment <byte>(((HeapMemory)memoryPointers.Pointers[i]).Buffer, (int)((HeapMemory)memoryPointers.Pointers[i]).VirtualOffset, (int)((HeapMemory)memoryPointers.Pointers[i]).VirtualCount), noDelay, headerSize); } } if (dealloc) { // DeAlloc the memory again. This is done for unreliable channels that dont need the message after the initial send. for (int i = 0; i < memoryPointers.VirtualCount; i++) { memoryManager.DeAlloc(((HeapMemory)memoryPointers.Pointers[i])); } } // Dealloc the array always. memoryManager.DeAlloc(memoryPointers); } }
public void CreateOutgoingMessage(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; } // Allocate the memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count + 2); // Write headers memory.Buffer[0] = HeaderPacker.Pack(MessageType.Data); memory.Buffer[1] = channelId; // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 2, payload.Count); // 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 dealloc the memory as the channel no longer needs it. ChannelRouter.SendMessage(pointers, true, connection, noMerge, memoryManager); }
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) { // We have already acked this message. Ack again SendAck(sequence); return(null); } else { // This is a future packet // Add to sequencer _incomingLowestAckedSequence = sequence; // Send ack SendAck(sequence); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc a memory wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } } }
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 || _incomingAckedSequences.Contains(sequence)) { // We have already acked this message. Ack again SendAck(sequence); return(null); } else if (sequence == _incomingLowestAckedSequence + 1) { // This is the "next" packet do { // Remove previous _incomingAckedSequences.Remove(_incomingLowestAckedSequence); _incomingLowestAckedSequence++; }while (_incomingAckedSequences.Contains((ushort)(_incomingLowestAckedSequence + 1))); // Ack the new message SendAck(sequence); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc a memory wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } else if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) > 0 && !_incomingAckedSequences.Contains(sequence)) { // This is a future packet // Add to sequencer _incomingAckedSequences.Add(sequence); // Ack the new message SendAck(sequence); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc a memory wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } return(null); } }
public void CreateOutgoingMessage(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; } lock (_sendLock) { // 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); if (_lastOutgoingPacket != null) { // Dealloc the last packet _lastOutgoingPacket.Value.DeAlloc(memoryManager); } // Add the memory to pending _lastOutgoingPacket = new PendingOutgoingPacketSequence() { Sequence = _lastOutgoingSequence, 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); } }
public HeapPointers HandleIncomingMessagePoll(ArraySegment <byte> payload) { // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset, payload.Count)); return(pointers); }
public HeapPointers CreateOutgoingMessage(ArraySegment <byte> payload, out byte headerSize, out bool dealloc) { 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 + "]"); } dealloc = false; headerSize = 0; return(null); } lock (_lock) { // Increment the sequence number _lastOutboundSequenceNumber++; // Set header size headerSize = 4; // Allocate the memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count + 4); // Write headers memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Data, false); memory.Buffer[1] = channelId; // Write the sequence memory.Buffer[2] = (byte)_lastOutboundSequenceNumber; memory.Buffer[3] = (byte)(_lastOutboundSequenceNumber >> 8); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 4, payload.Count); // Tell the caller to deallc the memory dealloc = true; // Allocate pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Point the first pointer to the memory pointers.Pointers[pointers.VirtualOffset] = memory; return(pointers); } }
public void DeAlloc(MemoryManager memoryManager) { if (IsAlloced) { for (int i = 0; i < Fragments.VirtualCount; i++) { if (Fragments.Pointers[Fragments.VirtualOffset + i] != null) { memoryManager.DeAlloc((HeapMemory)Fragments.Pointers[Fragments.VirtualOffset + i]); Fragments.Pointers[Fragments.VirtualOffset + i] = null; } } memoryManager.DeAlloc(Fragments); Fragments = null; } }
public HeapPointers HandleIncomingMessagePoll(ArraySegment <byte> payload) { // -> Sorting happens in the ConcurrentCircularQueue when messages are Enqueued // // Read the sequence number ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8)); lock (_receiveLock) { if (_incomingAckedPackets.Contains(sequence)) { // We have already received this message. Ignore it. return(null); } /* if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0) * { * if (SequencingUtils.Distance(sequence, _lastPollSequence, sizeof(ushort)) > 0) * { * Logging.LogInfo("Skipping dropping packet since it's newer than _lastPollSequence (" + _lastPollSequence + ") | _incomingLowestAckedSequence: " + _incomingLowestAckedSequence + " | currentSequence: " + sequence); * } * } */ if (SequencingUtils.Distance(sequence, _lastPollSequence, sizeof(ushort)) > 0) // ! We're using _lastPollSequence instead of _incomingLowestAckedSequence to not drop old packets if we're still waiting for them to be polled by the game/MLAPI, which can still be sorted properly { // Set the new sequence _incomingLowestAckedSequence = sequence; _incomingAckedPackets.Set(sequence, true); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(new ArraySegment <byte>(payload.Array, payload.Offset + 2, payload.Count - 2)); return(pointers); } return(null); } }
internal void HandleHeartbeat(ArraySegment <byte> payload) { _stateLock.EnterReadLock(); try { if (State == ConnectionState.Connected) { HeapPointers pointers = HeartbeatChannel.HandleIncomingMessagePoll(payload); if (pointers != null) { MemoryWrapper wrapper = (MemoryWrapper)pointers.Pointers[0]; if (wrapper != null) { if (wrapper.AllocatedMemory != null) { LastMessageIn = NetTime.Now; // Dealloc the memory MemoryManager.DeAlloc(wrapper.AllocatedMemory); } if (wrapper.DirectMemory != null) { LastMessageIn = NetTime.Now; } // Dealloc the wrapper MemoryManager.DeAlloc(wrapper); } // Dealloc the pointers MemoryManager.DeAlloc(pointers); } } } finally { _stateLock.ExitReadLock(); } }
internal static void SendMessage(HeapPointers pointers, bool dealloc, Connection connection, bool noMerge, MemoryManager memoryManager) { for (int i = 0; i < pointers.VirtualCount; i++) { connection.SendInternal(new ArraySegment <byte>(((HeapMemory)pointers.Pointers[i]).Buffer, (int)((HeapMemory)pointers.Pointers[i]).VirtualOffset, (int)((HeapMemory)pointers.Pointers[i]).VirtualCount), noMerge); } if (dealloc) { // DeAlloc the memory again. This is done for unreliable channels that dont need the message after the initial send. for (int i = 0; i < pointers.VirtualCount; i++) { memoryManager.DeAlloc(((HeapMemory)pointers.Pointers[i])); } } // Dealloc the array always. memoryManager.DeAlloc(pointers); }
public void DeAlloc(MemoryManager memoryManager) { if (IsAlloced) { for (int i = 0; i < Fragments.VirtualCount; i++) { if (Fragments.Pointers[Fragments.VirtualOffset + i] != null) { PendingOutgoingFragment fragment = (PendingOutgoingFragment)Fragments.Pointers[Fragments.VirtualOffset + i]; // Dealloc the fragment fragment.DeAlloc(memoryManager); Fragments.Pointers[Fragments.VirtualOffset + i] = null; } } // Dealloc the pointers memoryManager.DeAlloc(Fragments); Fragments = null; } }
public HeapPointers CreateOutgoingMessage(ArraySegment <byte> payload, out byte headerSize, out bool dealloc) { 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 + "]"); } dealloc = false; headerSize = 0; return(null); } lock (_lock) { PendingOutgoingPacket unsafeOutgoing = _sendSequencer.GetUnsafe(_lastOutboundSequenceNumber + 1, out bool isSafe); if (unsafeOutgoing.Alive && !isSafe) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("Outgoing packet window is exhausted. Disconnecting"); } connection.Disconnect(false); dealloc = false; headerSize = 0; return(null); } // Increment the sequence number _lastOutboundSequenceNumber++; // Set header size headerSize = 4; // Allocate the memory HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count + 4); // Write headers memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Data, false); memory.Buffer[1] = channelId; // Write the sequence memory.Buffer[2] = (byte)_lastOutboundSequenceNumber; memory.Buffer[3] = (byte)(_lastOutboundSequenceNumber >> 8); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 4, payload.Count); // Add the memory to pending _sendSequencer[_lastOutboundSequenceNumber] = new PendingOutgoingPacket() { Alive = true, Sequence = _lastOutboundSequenceNumber, Attempts = 1, LastSent = DateTime.Now, FirstSent = DateTime.Now, Memory = memory }; // Tell the caller NOT to dealloc the memory, the channel needs it for resend purposes. dealloc = false; // Allocate pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Point the first pointer to the memory pointers.Pointers[pointers.VirtualOffset] = memory; return(pointers); } }
public HeapPointers HandleIncomingMessagePoll(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; if (fragment >= config.MaxFragments) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("FragmentId was too large. [FragmentId=" + fragment + "] [Config.MaxFragments=" + config.MaxFragments + "]. The fragment was silently dropped, expect a timeout."); } return(null); } lock (_receiveLock) { // If the sequence is older than the last one we sent to user OR the packet is already acked OR the sequence is alive and its complete OR (the sequence is alive AND the fragments is alloced AND the alloced fragment count is larger than the fragment (I.E, the fragment is actually alloced) AND the fragment is not null AND the fragment is not dead)) if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0 || (_incomingAckedSequences.Contains(sequence)) || (_receiveSequencer.TryGet(sequence, out PendingIncomingPacketFragmented value) && (value.IsComplete || (value.Fragments.VirtualCount > fragment && value.Fragments.Pointers[value.Fragments.VirtualOffset + fragment] != null)))) { // We have already acked this message. Ack again SendAckEncoded(sequence, encodedFragment); return(null); } else { // This is a packet after the last. One that is not yet completed 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.TryGet(sequence, out value)) { // If this is the first fragment we ever get, index the data. HeapPointers fragmentPointers = memoryManager.AllocHeapPointers((uint)fragment + 1); value = new PendingIncomingPacketFragmented() { Fragments = fragmentPointers, Size = isFinal ? (ushort?)(fragment + 1) : null }; _receiveSequencer.Set(sequence, value); } else { // If the first fragment we got was fragment 1 / 500. The fragments array will only be of size 128. We need to potentially resize it if (value.Fragments.Pointers.Length - value.Fragments.VirtualOffset <= fragment) { // We need to expand the fragments array. // Alloc new array HeapPointers newPointers = memoryManager.AllocHeapPointers((uint)fragment + 1); // Copy old values Array.Copy(value.Fragments.Pointers, newPointers.Pointers, value.Fragments.Pointers.Length); // Return the memory for the old memoryManager.DeAlloc(value.Fragments); // Update the index value = new PendingIncomingPacketFragmented() { Fragments = newPointers, Size = isFinal ? (ushort?)(fragment + 1) : value.Size }; _receiveSequencer.Update(sequence, value); } // We might also have to expand the virtual count if (value.Fragments.VirtualCount <= fragment) { // Update the new virtual count value.Fragments.VirtualCount = (uint)fragment + 1; // Update the struct to set the size if it has changed (TODO: Check if needed) value = new PendingIncomingPacketFragmented() { Fragments = value.Fragments, Size = isFinal ? (ushort?)(fragment + 1) : value.Size }; _receiveSequencer.Update(sequence, value); } } // If the fragment is null OR the fragment is DEAD if (value.Fragments.Pointers[value.Fragments.VirtualOffset + fragment] == null) { // Alloc some memory for the fragment HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count - 4); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset + 4, memory.Buffer, 0, payload.Count - 4); // Add fragment to index value.Fragments.Pointers[value.Fragments.VirtualOffset + fragment] = memory; } // Send ack SendAckEncoded(sequence, encodedFragment); // If this sequence just completed. Return the memory if (value.IsComplete) { if (sequence == (ushort)(_incomingLowestAckedSequence + 1)) { // This is the next packet. do { // Remove previous _incomingAckedSequences.Remove(_incomingLowestAckedSequence); _incomingLowestAckedSequence++; }while (_incomingAckedSequences.Contains((ushort)(_incomingLowestAckedSequence + 1))); } else { // This is a future one _incomingAckedSequences.Add(sequence); } // Get the total size of all fragments uint totalSize = value.TotalByteSize; // Alloc memory for that large segment HeapMemory memory = memoryManager.AllocHeapMemory(totalSize); // Keep track of where we are, fragments COULD have different sizes. int bufferPosition = 0; if (value.Fragments != null) { // Copy all the parts for (int i = 0; i < value.Fragments.VirtualCount; i++) { // Copy fragment to final buffer Buffer.BlockCopy(((HeapMemory)value.Fragments.Pointers[value.Fragments.VirtualOffset + i]).Buffer, (int)((HeapMemory)value.Fragments.Pointers[value.Fragments.VirtualOffset + i]).VirtualOffset, memory.Buffer, bufferPosition, (int)((HeapMemory)value.Fragments.Pointers[value.Fragments.VirtualOffset + i]).VirtualCount); bufferPosition += (int)((HeapMemory)value.Fragments.Pointers[value.Fragments.VirtualOffset + i]).VirtualCount; } } // Free the memory of all the individual fragments value.DeAlloc(memoryManager); // Kill _receiveSequencer.Remove(sequence); // Alloc pointers HeapPointers pointers = memoryManager.AllocHeapPointers(1); // Alloc a memory wrapper pointers.Pointers[0] = memoryManager.AllocMemoryWrapper(memory); return(pointers); } return(null); } } }
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); } }
public void TestOutOfPacketOrderMultiFragment() { Configuration.SocketConfig config = new Configuration.SocketConfig() { MinimumMTU = 1050 }; MemoryManager memoryManager = new MemoryManager(config); Connection clientsConnectionToServer = Connection.Stub(config, memoryManager); Connection serversConnectionToClient = Connection.Stub(config, memoryManager); ReliableSequencedFragmentedChannel clientChannel = new ReliableSequencedFragmentedChannel(0, clientsConnectionToServer, config, memoryManager); ReliableSequencedFragmentedChannel serverChannel = new ReliableSequencedFragmentedChannel(0, serversConnectionToClient, config, memoryManager); // Create 3 payloads byte[] message1 = BufferHelper.GetRandomBuffer(1024 * 5, 0); byte[] message2 = BufferHelper.GetRandomBuffer(1024 * 8, 1); byte[] message3 = BufferHelper.GetRandomBuffer(1024 * 6, 2); // Sequence all payloads as outgoing HeapPointers message1Memory = clientChannel.CreateOutgoingMessage(new ArraySegment <byte>(message1, 0, 1024 * 5), out _, out bool dealloc); HeapPointers message2Memory = clientChannel.CreateOutgoingMessage(new ArraySegment <byte>(message2, 0, 1024 * 8), out _, out dealloc); HeapPointers message3Memory = clientChannel.CreateOutgoingMessage(new ArraySegment <byte>(message3, 0, 1024 * 6), out _, out dealloc); int message1MemoryLength = (int)message1Memory.VirtualCount; int message2MemoryLength = (int)message2Memory.VirtualCount; int message3MemoryLength = (int)message3Memory.VirtualCount; // Consume 1st payload bool[] hasMore1s = new bool[message1MemoryLength]; ArraySegment <byte>?[] payload1s = new ArraySegment <byte>?[message1MemoryLength]; for (int i = 0; i < message1MemoryLength; i++) { payload1s[i] = serverChannel.HandleIncomingMessagePoll(new ArraySegment <byte>(((HeapMemory)message1Memory.Pointers[i]).Buffer, (int)((HeapMemory)message1Memory.Pointers[i]).VirtualOffset + 2, (int)((HeapMemory)message1Memory.Pointers[i]).VirtualCount - 2), out _, out bool hasMore1); hasMore1s[i] = hasMore1; } // Consume 3rd payload bool[] hasMore3s = new bool[message3MemoryLength]; ArraySegment <byte>?[] payload3s = new ArraySegment <byte>?[message3MemoryLength]; for (int i = 0; i < message3MemoryLength; i++) { payload3s[i] = serverChannel.HandleIncomingMessagePoll(new ArraySegment <byte>(((HeapMemory)message3Memory.Pointers[i]).Buffer, (int)((HeapMemory)message3Memory.Pointers[i]).VirtualOffset + 2, (int)((HeapMemory)message3Memory.Pointers[i]).VirtualCount - 2), out _, out bool hasMore3); hasMore3s[i] = hasMore3; } // Consume 2nd payload bool[] hasMore2s = new bool[message2MemoryLength]; ArraySegment <byte>?[] payload2s = new ArraySegment <byte>?[message2MemoryLength]; for (int i = 0; i < message2MemoryLength; i++) { payload2s[i] = serverChannel.HandleIncomingMessagePoll(new ArraySegment <byte>(((HeapMemory)message2Memory.Pointers[i]).Buffer, (int)((HeapMemory)message2Memory.Pointers[i]).VirtualOffset + 2, (int)((HeapMemory)message2Memory.Pointers[i]).VirtualCount - 2), out _, out bool hasMore2); hasMore2s[i] = hasMore2; } for (int i = 0; i < payload1s.Length; i++) { Assert.Null(payload1s[i]); } for (int i = 0; i < payload3s.Length; i++) { Assert.Null(payload3s[i]); } for (int i = 0; i < payload2s.Length; i++) { Assert.Null(payload2s[i]); } // TODO: Assert HasMore states HeapMemory poll1 = serverChannel.HandlePoll(); HeapMemory poll2 = serverChannel.HandlePoll(); HeapMemory poll3 = serverChannel.HandlePoll(); Assert.NotNull(poll1); Assert.NotNull(poll2); Assert.NotNull(poll3); { Assert.That(BufferHelper.ValidateBufferSizeAndIdentity(poll1, 0, 1024 * 5)); } { Assert.That(BufferHelper.ValidateBufferSizeAndIdentity(poll2, 1, 1024 * 8)); } { Assert.That(BufferHelper.ValidateBufferSizeAndIdentity(poll3, 2, 1024 * 6)); } }
internal static void HandleIncomingMessage(ArraySegment <byte> payload, Connection connection, SocketConfig config, MemoryManager memoryManager) { // This is where all data packets arrive after passing the connection handling. byte channelId = payload.Array[payload.Offset]; if (channelId < 0 || channelId >= connection.Channels.Length) { // ChannelId out of range if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Got message on channel out of range. [ChannelId=" + channelId + "]"); } return; } IChannel channel = connection.Channels[channelId]; if (channel != null) { HeapPointers incomingPointers = channel.HandleIncomingMessagePoll(new ArraySegment <byte>(payload.Array, payload.Offset + 1, payload.Count - 1)); if (incomingPointers != null) { ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8)); // TODO: This is already calculated inside HandleIncomingMessagePoll, we could just get it from there instead of doing this again... // There is new packets for (int i = 0; i < incomingPointers.VirtualCount; i++) { MemoryWrapper wrapper = (MemoryWrapper)incomingPointers.Pointers[i]; HeapMemory memory = null; if (wrapper.AllocatedMemory != null) { memory = wrapper.AllocatedMemory; } if (wrapper.DirectMemory != null) { // Alloc memory memory = memoryManager.AllocHeapMemory((uint)wrapper.DirectMemory.Value.Count); // Copy payload to borrowed memory Buffer.BlockCopy(wrapper.DirectMemory.Value.Array, wrapper.DirectMemory.Value.Offset, memory.Buffer, 0, wrapper.DirectMemory.Value.Count); } if (memory != null) { // Send to userspace connection.Socket.PublishEvent(new NetworkEvent() { Connection = connection, Socket = connection.Socket, Type = NetworkEventType.Data, AllowUserRecycle = true, Data = new ArraySegment <byte>(memory.Buffer, (int)memory.VirtualOffset, (int)memory.VirtualCount), InternalMemory = memory, SocketReceiveTime = NetTime.Now, ChannelId = channelId, Sequence = sequence, MemoryManager = memoryManager, EndPoint = connection.EndPoint }); } // Dealloc the wrapper memoryManager.DeAlloc(wrapper); } // Dealloc the pointers memoryManager.DeAlloc(incomingPointers); } } else { if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Receive message failed because the channel is not assigned"); } } }
public ArraySegment <byte>?HandleIncomingMessagePoll(ArraySegment <byte> payload, out byte headerBytes, out bool hasMore) { // 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; // Set the headerBytes headerBytes = 4; if (fragment > config.MaxFragments) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("FragmentId was too large. [FragmentId=" + fragment + "] [Config.MaxFragments=" + config.MaxFragments + "]. The fragment was silently dropped, expect a timeout."); } hasMore = false; return(null); } lock (_lock) { if (SequencingUtils.Distance(sequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0 || (_receiveSequencer[sequence].Alive && _receiveSequencer[sequence].IsComplete) || (_receiveSequencer[sequence].Alive && _receiveSequencer[sequence].Fragments != null && _receiveSequencer[sequence].Fragments.VirtualCount > fragment && _receiveSequencer[sequence].Fragments.Pointers[_receiveSequencer[sequence].Fragments.VirtualOffset + fragment] != null && !((HeapMemory)_receiveSequencer[sequence].Fragments.Pointers[_receiveSequencer[sequence].Fragments.VirtualOffset + fragment]).isDead)) { // We have already acked this message. Ack again connection.IncomingDuplicatePackets++; connection.IncomingDuplicateUserBytes += (ulong)payload.Count - 2; connection.IncomingDuplicateTotalBytes += (ulong)payload.Count + 2; SendAckEncoded(sequence, encodedFragment); hasMore = false; return(null); } else { // This is a packet after the last. One that is not yet completed PendingIncomingPacket unsafeIncoming = _receiveSequencer.GetUnsafe(sequence, out bool isSafe); if (unsafeIncoming.Alive && !isSafe) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("Incoming packet window is exhausted. Disconnecting"); } connection.Disconnect(false); hasMore = false; return(null); } else if (!_receiveSequencer[sequence].Alive) { // If this is the first fragment we ever get, index the data. // TODO: Alloc more size and just expand later in the pointer array HeapPointers fragmentPointers = memoryManager.AllocHeapPointers((uint)fragment + 1); _receiveSequencer[sequence] = new PendingIncomingPacket() { Alive = true, Fragments = fragmentPointers, Sequence = sequence, Size = isFinal ? (ushort?)(fragment + 1) : null }; } else { // If the first fragment we got was fragment 1 / 500. The fragments array will only be of size 128. We need to potentially resize it if (_receiveSequencer[sequence].Fragments.Pointers.Length - _receiveSequencer[sequence].Fragments.VirtualOffset <= fragment) { // We need to expand the fragments array. // Alloc new array HeapPointers newPointers = memoryManager.AllocHeapPointers((uint)fragment + 1); // Copy old values Array.Copy(_receiveSequencer[sequence].Fragments.Pointers, newPointers.Pointers, _receiveSequencer[sequence].Fragments.Pointers.Length); // Return the memory for the old memoryManager.DeAlloc(_receiveSequencer[sequence].Fragments); // Update the index _receiveSequencer[sequence] = new PendingIncomingPacket() { Fragments = newPointers, Alive = _receiveSequencer[sequence].Alive, Sequence = _receiveSequencer[sequence].Sequence, Size = isFinal ? (ushort?)(fragment + 1) : _receiveSequencer[sequence].Size }; } // We might also have to expand the virtual count if (_receiveSequencer[sequence].Fragments.VirtualCount <= fragment) { // Update the new virtual count _receiveSequencer[sequence].Fragments.VirtualCount = (uint)fragment + 1; // Update the struct to set the size if it has changed (TODO: Check if needed) _receiveSequencer[sequence] = new PendingIncomingPacket() { Fragments = _receiveSequencer[sequence].Fragments, Alive = _receiveSequencer[sequence].Alive, Sequence = _receiveSequencer[sequence].Sequence, Size = isFinal ? (ushort?)(fragment + 1) : _receiveSequencer[sequence].Size }; } } if (_receiveSequencer[sequence].Fragments.Pointers[_receiveSequencer[sequence].Fragments.VirtualOffset + fragment] == null || ((HeapMemory)_receiveSequencer[sequence].Fragments.Pointers[_receiveSequencer[sequence].Fragments.VirtualOffset + fragment]).isDead) { // Alloc some memory for the fragment HeapMemory memory = memoryManager.AllocHeapMemory((uint)payload.Count - 4); // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset + 4, memory.Buffer, 0, payload.Count - 4); // Add fragment to index _receiveSequencer[sequence].Fragments.Pointers[_receiveSequencer[sequence].Fragments.VirtualOffset + fragment] = memory; } // Send ack SendAckEncoded(sequence, encodedFragment); // Sequenced never returns the original memory. Thus we need to return null and tell the caller to Poll instead. hasMore = _receiveSequencer[_incomingLowestAckedSequence + 1].Alive && _receiveSequencer[_incomingLowestAckedSequence + 1].IsComplete; return(null); } } }
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); } }
public HeapPointers CreateOutgoingMessage(ArraySegment <byte> payload, out byte headerSize, out bool dealloc) { // 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 + "]"); } dealloc = false; headerSize = 0; return(null); } lock (_lock) { PendingOutgoingPacket unsafeOutgoing = _sendSequencer.GetUnsafe(_lastOutboundSequenceNumber + 1, out bool isSafe); if (unsafeOutgoing.Alive && !isSafe) { if (Logging.CurrentLogLevel <= LogLevel.Error) { Logging.LogError("Outgoing packet window is exhausted. Disconnecting"); } connection.Disconnect(false); dealloc = false; headerSize = 0; return(null); } // Increment the sequence number _lastOutboundSequenceNumber++; // Set header size headerSize = 6; // 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((byte)MessageType.Data, false); ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[1] = channelId; // Write the sequence ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[2] = (byte)_lastOutboundSequenceNumber; ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]).Buffer[3] = (byte)(_lastOutboundSequenceNumber >> 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; } // Tell the caller NOT to dealloc the memory, the channel needs it for resend purposes. dealloc = false; // 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() { Alive = true, Attempts = 1, LastSent = DateTime.Now, FirstSent = DateTime.Now, Sequence = _lastOutboundSequenceNumber, Memory = ((HeapMemory)memoryParts.Pointers[memoryParts.VirtualOffset + i]) }; } // Add the memory to the outgoing sequencer _sendSequencer[_lastOutboundSequenceNumber] = new PendingOutgoingPacket() { Alive = true, Fragments = outgoingFragments }; return(memoryParts); } }
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); } }