public void HandleAck(ArraySegment <byte> payload) { // Read the sequence number ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8)); if (_sendSequencer[sequence].Alive) { // Dealloc the memory held by the sequencer MemoryManager.DeAlloc(_sendSequencer[sequence].Memory); // TODO: Remove roundtripping from channeled packets and make specific ping-pong packets // Get the roundtrp ulong roundtrip = (ulong)Math.Round((DateTime.Now - _sendSequencer[sequence].FirstSent).TotalMilliseconds); // Report to the connection connection.AddRoundtripSample(roundtrip); // Kill the packet _sendSequencer[sequence] = new PendingOutgoingPacket() { Alive = false, Sequence = sequence }; } for (ushort i = sequence; _sendSequencer[i].Alive; i++) { _incomingLowestAckedSequence = i; } }
public void InternalUpdate() { long distance = SequencingUtils.Distance(_lastOutboundSequenceNumber, _incomingLowestAckedSequence, sizeof(ushort)); for (ushort i = _incomingLowestAckedSequence; i < _incomingLowestAckedSequence + distance; i++) { if (_sendSequencer[i].Alive) { if (_sendSequencer[i].Attempts > config.ReliabilityMaxResendAttempts) { // If they don't ack the message, disconnect them connection.Disconnect(false); } else if ((DateTime.Now - _sendSequencer[i].LastSent).TotalMilliseconds > connection.Roundtrip * config.ReliabilityResendRoundtripMultiplier) { _sendSequencer[i] = new PendingOutgoingPacket() { Alive = true, Attempts = (ushort)(_sendSequencer[i].Attempts + 1), LastSent = DateTime.Now, FirstSent = _sendSequencer[i].FirstSent, Memory = _sendSequencer[i].Memory, Sequence = i }; connection.SendRaw(new ArraySegment <byte>(_sendSequencer[i].Memory.Buffer, (int)_sendSequencer[i].Memory.VirtualOffset, (int)_sendSequencer[i].Memory.VirtualCount), false); } } } }
private void HandleAck(ushort sequence) { lock (_lock) { if (_sendSequencer[sequence].Alive) { // Add statistics connection.OutgoingConfirmedPackets++; // Dealloc the memory held by the sequencer memoryManager.DeAlloc(_sendSequencer[sequence].Memory); // TODO: Remove roundtripping from channeled packets and make specific ping-pong packets // Get the roundtrp ulong roundtrip = (ulong)Math.Round((DateTime.Now - _sendSequencer[sequence].FirstSent).TotalMilliseconds); // Report to the connection connection.AddRoundtripSample(roundtrip); // Kill the packet _sendSequencer[sequence] = new PendingOutgoingPacket() { Alive = false, Sequence = sequence }; } for (ushort i = sequence; _sendSequencer[i].Alive; i++) { _incomingLowestAckedSequence = i; } } }
public HeapMemory CreateOutgoingMessage(ArraySegment <byte> payload, out bool dealloc) { // Increment the sequence number _lastOutboundSequenceNumber++; // Allocate the memory HeapMemory memory = MemoryManager.Alloc((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; return(memory); }
public void InternalUpdate() { lock (_lock) { if (_lastOutgoingPacket.Alive) { if (_lastOutgoingPacket.Attempts > config.ReliabilityMaxResendAttempts) { // If they don't ack the message, disconnect them connection.Disconnect(false); return; } else if ((DateTime.Now - _lastOutgoingPacket.LastSent).TotalMilliseconds > connection.SmoothRoundtrip * config.ReliabilityResendRoundtripMultiplier) { _lastOutgoingPacket = new PendingOutgoingPacket() { Alive = true, Attempts = (ushort)(_lastOutgoingPacket.Attempts + 1), LastSent = DateTime.Now, FirstSent = _lastOutgoingPacket.FirstSent, Memory = _lastOutgoingPacket.Memory, Sequence = _lastOutgoingPacket.Sequence }; connection.SendRaw(new ArraySegment <byte>(_lastOutgoingPacket.Memory.Buffer, (int)_lastOutgoingPacket.Memory.VirtualOffset, (int)_lastOutgoingPacket.Memory.VirtualCount), false, 4); connection.OutgoingResentPackets++; } } } }
public void HandleAck(ArraySegment <byte> payload) { // Read the sequence number ushort sequence = (ushort)(payload.Array[payload.Offset] | (ushort)(payload.Array[payload.Offset + 1] << 8)); lock (_lock) { if (_lastOutgoingPacket.Alive && _lastOutgoingPacket.Sequence == sequence) { // Add statistics connection.OutgoingConfirmedPackets++; // Dealloc the memory held by the last packet _lastOutgoingPacket.DeAlloc(memoryManager); // TODO: Remove roundtripping from channeled packets and make specific ping-pong packets // Get the roundtrp ulong roundtrip = (ulong)Math.Round((DateTime.Now - _lastOutgoingPacket.FirstSent).TotalMilliseconds); // Report to the connection connection.AddRoundtripSample(roundtrip); // Kill the packet _lastOutgoingPacket = new PendingOutgoingPacket() { Alive = false, Sequence = sequence }; _incomingLowestAckedSequence = sequence; } } }
internal ReliableOrderedChannel(byte channelId, Connection connection, SocketConfig config, MemoryManager memoryManager) { this.channelId = channelId; this.connection = connection; this.config = config; this.memoryManager = memoryManager; _lastOutgoingPacket = new PendingOutgoingPacket() { Alive = false, Attempts = 0, FirstSent = DateTime.MinValue, LastSent = DateTime.MinValue, Memory = null, Sequence = 0 }; _lastAckTimes = new SlidingWindow <DateTime>(config.ReliableAckFlowWindowSize, true, sizeof(ushort)); }
public void Reset() { lock (_lock) { // Clear all incoming states _incomingLowestAckedSequence = 0; // Clear all outgoing states _lastOutboundSequenceNumber = 0; // Reset the outgoing packet _lastOutgoingPacket = new PendingOutgoingPacket() { Alive = false, Attempts = 0, FirstSent = DateTime.MinValue, LastSent = DateTime.MinValue, Memory = null, Sequence = 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 (_lock) { if (_sendSequencer[sequence].Alive && _sendSequencer[sequence].Fragments.VirtualCount > fragment && ((PendingOutgoingFragment)_sendSequencer[sequence].Fragments.Pointers[fragment]).Alive) { // Add statistics connection.OutgoingConfirmedPackets++; // Dealloc the memory held by the sequencer for the packet ((PendingOutgoingFragment)_sendSequencer[sequence].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((DateTime.Now - ((PendingOutgoingFragment)_sendSequencer[sequence].Fragments.Pointers[fragment]).FirstSent).TotalMilliseconds); // Report to the connection connection.AddRoundtripSample(roundtrip); // Kill the fragment packet _sendSequencer[sequence].Fragments.Pointers[fragment] = new PendingOutgoingFragment() { Alive = false, Sequence = sequence }; bool hasAllocatedAndAliveFragments = false; for (int i = 0; i < _sendSequencer[sequence].Fragments.VirtualCount; i++) { if (_sendSequencer[sequence].Fragments.Pointers[i] != null && ((PendingOutgoingFragment)_sendSequencer[sequence].Fragments.Pointers[i]).Alive) { hasAllocatedAndAliveFragments = true; break; } } if (!hasAllocatedAndAliveFragments) { // Dealloc the wrapper packet _sendSequencer[sequence].DeAlloc(memoryManager); // Kill the wrapper packet _sendSequencer[sequence] = new PendingOutgoingPacket() { Alive = false }; } } for (ushort i = sequence; _sendSequencer[i].Alive && _sendSequencer[i].AllFragmentsAlive; i++) { _incomingLowestAckedSequence = i; } } }
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 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); } }