internal void HandleMTURequest(uint size) { // This does not access anything shared. We thus dont need to lock the state. If the state is raced or outdated its no harm done. // TODO: Remove lock _stateLock.EnterReadLock(); try { if (State == ConnectionState.Connected) { // Alloc memory for response HeapMemory memory = MemoryManager.AllocHeapMemory(size); // Write the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.MTUResponse); // Send the response SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Dealloc the memory MemoryManager.DeAlloc(memory); } } finally { _stateLock.ExitReadLock(); } }
private void SendAck(ushort sequence) { // Check the last ack time if (!_lastAckTimes.TryGet(sequence, out NetTime value) || ((NetTime.Now - value).TotalMilliseconds > connection.SmoothRoundtrip * config.ReliabilityResendRoundtripMultiplier && (NetTime.Now - value).TotalMilliseconds > config.ReliabilityMinAckResendDelay)) { // Set the last ack time _lastAckTimes.Set(sequence, NetTime.Now); // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack(MessageType.Ack); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); // Send ack connection.SendInternal(new ArraySegment <byte>(ackMemory.Buffer, 0, 4), false); // Return memory memoryManager.DeAlloc(ackMemory); } }
internal void SendHail() { HailStatus.Attempts++; HailStatus.LastAttempt = NetTime.Now; // Packet size int size = 2 + (byte)Config.ChannelTypes.Length; // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory((uint)size); // Write the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.Hail); // Write the amount of channels memory.Buffer[1] = (byte)Config.ChannelTypes.Length; // Write the channel types for (byte i = 0; i < (byte)Config.ChannelTypes.Length; i++) { memory.Buffer[2 + i] = ChannelTypeUtils.ToByte(Config.ChannelTypes[i]); } // Send the response SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); // Print Debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Client " + EndPoint + " was sent a hail"); } }
internal void SendChallengeRequest() { // Set resend values HandshakeResendAttempts++; HandshakeLastSendTime = NetTime.Now; // Packet size uint size = 1 + sizeof(ulong) + 1; // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory(size); // Write the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.ChallengeRequest); // Write connection challenge for (byte i = 0; i < sizeof(ulong); i++) { memory.Buffer[1 + i] = ((byte)(ConnectionChallenge >> (i * 8))); } // Write the challenge difficulty memory.Buffer[1 + sizeof(ulong)] = (byte)ChallengeDifficulty; // Send the challenge SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Client " + EndPoint + " was sent a challenge of difficulty " + ChallengeDifficulty); } }
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); }
private void SendAck(ushort sequence) { // Check the last ack time if ((DateTime.Now - _lastAckTimes[sequence]).TotalMilliseconds > connection.SmoothRoundtrip * config.ReliabilityResendRoundtripMultiplier) { // Set the last ack time _lastAckTimes[sequence] = DateTime.Now; // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Ack, false); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); // Send ack connection.SendRaw(new ArraySegment <byte>(ackMemory.Buffer, 0, 4), false, 4); // Return memory memoryManager.DeAlloc(ackMemory); } }
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 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); } }
private void SendAck(ushort sequence) { // Check the last ack time if (!_lastAckTimes.TryGet(sequence, out NetTime value) || ((NetTime.Now - value).TotalMilliseconds > connection.SmoothRoundtrip * config.ReliabilityResendRoundtripMultiplier && (NetTime.Now - value).TotalMilliseconds > config.ReliabilityMinAckResendDelay)) { // Set the last ack time _lastAckTimes.Set(sequence, NetTime.Now); // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4 + (uint)(config.EnableMergedAcks ? config.MergedAckBytes : 0)); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack(MessageType.Ack); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); if (config.EnableMergedAcks) { // Reset the memory for (int i = 0; i < config.MergedAckBytes; i++) { ackMemory.Buffer[4 + i] = 0; } // Set the bit fields for (int i = 0; i < config.MergedAckBytes * 8; i++) { ushort bitSequence = (ushort)(sequence - (i + 1)); bool bitAcked = SequencingUtils.Distance(bitSequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0 || _incomingAckedSequences.Contains(bitSequence); if (bitAcked) { // Set the ack time for this packet _lastAckTimes.Set(bitSequence, NetTime.Now); } // Write single ack bit ackMemory.Buffer[4 + (i / 8)] |= (byte)((bitAcked ? 1 : 0) << (7 - (i % 8))); } } // Send ack connection.SendInternal(new ArraySegment <byte>(ackMemory.Buffer, 0, 4 + (config.EnableMergedAcks ? config.MergedAckBytes : 0)), false); // Return memory memoryManager.DeAlloc(ackMemory); } }
private void SendAck(ushort sequence) { // Check the last ack time if ((DateTime.Now - _lastAckTimes[sequence]).TotalMilliseconds > connection.SmoothRoundtrip * config.ReliabilityResendRoundtripMultiplier) { // Set the last ack time _lastAckTimes[sequence] = DateTime.Now; // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4 + (uint)(config.EnableMergedAcks ? config.MergedAckBytes : 0)); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Ack, false); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); if (config.EnableMergedAcks) { // Reset the memory for (int i = 0; i < config.MergedAckBytes; i++) { ackMemory.Buffer[4 + i] = 0; } // Set the bit fields for (int i = 0; i < config.MergedAckBytes * 8; i++) { ushort bitSequence = (ushort)(sequence - (i + 1)); bool bitAcked = SequencingUtils.Distance(bitSequence, _incomingLowestAckedSequence, sizeof(ushort)) <= 0 || _receiveSequencer[bitSequence].Alive; if (bitAcked) { // Set the ack time for this packet _lastAckTimes[sequence] = DateTime.Now; } // Write single ack bit ackMemory.Buffer[4 + (i / 8)] |= (byte)((bitAcked ? 1 : 0) << (7 - (i % 8))); } } // Send ack connection.SendRaw(new ArraySegment <byte>(ackMemory.Buffer, 0, 4 + (config.EnableMergedAcks ? config.MergedAckBytes : 0)), false, (byte)(4 + (config.EnableMergedAcks ? config.MergedAckBytes : 0))); // Return memory memoryManager.DeAlloc(ackMemory); } }
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); } }
internal HeapMemory CreateOutgoingHeartbeatMessage() { // Increment the sequence number _lastOutboundSequenceNumber++; // Allocate the memory HeapMemory memory = MemoryManager.Alloc(3); // Write headers memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Heartbeat, false); // Write the sequence memory.Buffer[1] = (byte)_lastOutboundSequenceNumber; memory.Buffer[2] = (byte)(_lastOutboundSequenceNumber >> 8); return(memory); }
public HeapMemory CreateOutgoingMessage(ArraySegment <byte> payload, out bool dealloc) { // Allocate the memory HeapMemory memory = MemoryManager.Alloc((uint)payload.Count + 2); // Write headers memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Data, false); memory.Buffer[1] = channelId; // Copy the payload Buffer.BlockCopy(payload.Array, payload.Offset, memory.Buffer, 2, payload.Count); // Tell the caller to deallc the memory dealloc = true; return(memory); }
private void SendAck(ushort sequence) { // Alloc ack memory HeapMemory ackMemory = MemoryManager.Alloc(4); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.Ack, false); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); // Send ack connection.SendRaw(new ArraySegment <byte>(ackMemory.Buffer, 0, 4), false); // Return memory MemoryManager.DeAlloc(ackMemory); }
internal void SendHailConfirmed() { // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory(1); // Write the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.HailConfirmed); // Send confirmation SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Hail confirmation sent to " + EndPoint); } }
private void RunPathMTU() { _stateLock.EnterReadLock(); try { if (State == ConnectionState.Connected && MTU < Config.MaximumMTU && MTUStatus.Attempts < Config.MaxMTUAttempts && (NetTime.Now - MTUStatus.LastAttempt).TotalMilliseconds > Config.MTUAttemptDelay) { int attemptedMtu = (int)(MTU * Config.MTUGrowthFactor); if (attemptedMtu > Config.MaximumMTU) { attemptedMtu = Config.MaximumMTU; } if (attemptedMtu < Config.MinimumMTU) { attemptedMtu = Config.MinimumMTU; } MTUStatus.Attempts++; MTUStatus.LastAttempt = NetTime.Now; // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory((uint)attemptedMtu); // Set the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.MTURequest); // Send the request SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Dealloc the memory MemoryManager.DeAlloc(memory); } } finally { _stateLock.ExitReadLock(); } }
private void SendAck(ushort sequence, ushort fragment, bool isFinal) { // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack(MessageType.Ack); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); // Write fragment ackMemory.Buffer[4] = (byte)(fragment & 32767); ackMemory.Buffer[5] = (byte)(((byte)((fragment & 32767) >> 8)) | (byte)(isFinal ? 128 : 0)); // Send ack connection.SendInternal(new ArraySegment <byte>(ackMemory.Buffer, 0, 6), false); // Return memory memoryManager.DeAlloc(ackMemory); }
private void SendAckEncoded(ushort sequence, ushort encodedFragment) { // Alloc ack memory HeapMemory ackMemory = memoryManager.AllocHeapMemory(4); // Write header ackMemory.Buffer[0] = HeaderPacker.Pack(MessageType.Ack); ackMemory.Buffer[1] = (byte)channelId; // Write sequence ackMemory.Buffer[2] = (byte)sequence; ackMemory.Buffer[3] = (byte)(sequence >> 8); // Write fragment ackMemory.Buffer[4] = (byte)encodedFragment; ackMemory.Buffer[5] = (byte)(encodedFragment >> 8); // Send ack connection.SendInternal(new ArraySegment <byte>(ackMemory.Buffer, 0, 6), false); // Return memory memoryManager.DeAlloc(ackMemory); }
internal void SendChallengeResponse() { // Set resend values HandshakeResendAttempts++; HandshakeLastSendTime = NetTime.Now; // Calculate the minimum size we can fit the packet in int minSize = 1 + sizeof(ulong); // Calculate the actual size with respect to amplification padding int size = Math.Max(minSize, (int)Config.AmplificationPreventionHandshakePadding); // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory((uint)size); // Write the header memory.Buffer[0] = HeaderPacker.Pack(MessageType.ChallengeResponse); // Write the challenge response for (byte i = 0; i < sizeof(ulong); i++) { memory.Buffer[1 + i] = ((byte)(ChallengeAnswer >> (i * 8))); } // Send the challenge response SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); // Print debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Server " + EndPoint + " challenge of difficulty " + ChallengeDifficulty + " was solved. Answer was sent. [CollidedValue=" + ChallengeAnswer + "]"); } }
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); // Tell the caller to deallc the memory dealloc = true; return(memory); }
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 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); } }
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); } }
internal void DisconnectInternal(bool sendMessage, bool timeout) { #if ALLOW_CONNECTION_STUB if (IsStub) { // NOOP return; } #endif // TODO: Could be just a normal write lock. The chance of it not being a write in the end is small. _stateLock.EnterUpgradeableReadLock(); try { if (State == ConnectionState.Connected && sendMessage && !timeout) { // Send disconnect message // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory(1); // Write disconnect header memory.Buffer[0] = HeaderPacker.Pack(MessageType.Disconnect); // Send disconnect message SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); } if (State != ConnectionState.Disconnected) { _stateLock.EnterWriteLock(); try { // Set the state to disconnected State = ConnectionState.Disconnected; // Release all memory Release(); } finally { _stateLock.ExitWriteLock(); } // Remove from global lookup Socket.RemoveConnection(this); // Send disconnect to userspace Socket.PublishEvent(new NetworkEvent() { Connection = this, Socket = Socket, Type = timeout ? NetworkEventType.Timeout : NetworkEventType.Disconnect, AllowUserRecycle = false, ChannelId = 0, Data = new ArraySegment <byte>(), InternalMemory = null, SocketReceiveTime = NetTime.Now, MemoryManager = MemoryManager, EndPoint = EndPoint }); } } finally { _stateLock.ExitUpgradeableReadLock(); } }
private void CheckConnectionResends() { _stateLock.EnterReadLock(); try { switch (State) { case ConnectionState.RequestingConnection: { if ((!Config.TimeBasedConnectionChallenge || PreConnectionChallengeSolved) && (NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.ConnectionRequestMinResendDelay && HandshakeResendAttempts <= Config.MaxConnectionRequestResends) { HandshakeResendAttempts++; HandshakeLastSendTime = NetTime.Now; // Calculate the minimum size we can fit the packet in int minSize = 1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + (Config.TimeBasedConnectionChallenge ? sizeof(ulong) * 3 : 0); // Calculate the actual size with respect to amplification padding int size = Math.Max(minSize, (int)Config.AmplificationPreventionHandshakePadding); // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory((uint)size); // Write the header memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.ConnectionRequest); // Copy the identification token Buffer.BlockCopy(Constants.RUFFLES_PROTOCOL_IDENTIFICATION, 0, memory.Buffer, 1, Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length); if (Config.TimeBasedConnectionChallenge) { // Write the response unix time for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + x] = ((byte)(PreConnectionChallengeTimestamp >> (x * 8))); } // Write counter for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + sizeof(ulong) + x] = ((byte)(PreConnectionChallengeCounter >> (x * 8))); } // Write IV for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + (sizeof(ulong) * 2) + x] = ((byte)(PreConnectionChallengeIV >> (x * 8))); } // Print debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Resending ConnectionRequest with challenge [Counter=" + PreConnectionChallengeCounter + "] [IV=" + PreConnectionChallengeIV + "] [Time=" + PreConnectionChallengeTimestamp + "] [Hash=" + HashProvider.GetStableHash64(PreConnectionChallengeTimestamp, PreConnectionChallengeCounter, PreConnectionChallengeIV) + "]"); } } else { // Print debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Resending ConnectionRequest"); } } SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); } } break; case ConnectionState.RequestingChallenge: { if ((NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.HandshakeResendDelay && HandshakeResendAttempts <= Config.MaxHandshakeResends) { // Resend challenge request SendChallengeRequest(); } } break; case ConnectionState.SolvingChallenge: { if ((NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.HandshakeResendDelay && HandshakeResendAttempts <= Config.MaxHandshakeResends) { // Resend response SendChallengeResponse(); } } break; case ConnectionState.Connected: { if (!HailStatus.HasAcked && (NetTime.Now - HailStatus.LastAttempt).TotalMilliseconds > Config.HandshakeResendDelay && HailStatus.Attempts <= Config.MaxHandshakeResends) { // Resend hail SendHail(); } } break; } } finally { _stateLock.ExitReadLock(); } }
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); } }