internal RUDPACKPacket(IPEndPoint remoteEndPoint, int packetId, RUDPPacketChannel channel, bool isConnectionAccepted) { RemoteEndPoint = remoteEndPoint; Channel = channel; IsConnectionAccepted = isConnectionAccepted; PacketId = packetId; }
internal long TSReceived; // Time Stamp : when message is received internal RUDPIngoingPacket(RUDPSocket rudp, int packetId, byte[] payload, RUDPPacketChannel channel, long tsReceived) { RUDP = rudp; PacketId = packetId; Channel = channel; Payload = payload; TSReceived = tsReceived; }
internal RUDPOutgoingPacket(int packetId, long sequence, byte[] payload, RUDPPacketChannel channel) { PacketId = packetId; Sequence = sequence; Payload = payload; Channel = channel; Reset(); }
static public void Deallocate(RUDPPacketChannel channel, byte[] buffer) { if (_currentAllocation < MaxAllocation) { Queue <byte[]> queue = _queues[(int)channel]; ReaderWriterLockSlim rwlock = _locks[(int)channel]; rwlock.EnterWriteLock(); queue.Enqueue(buffer); rwlock.ExitWriteLock(); _currentAllocation += buffer.Length; } }
static public byte[] Allocate(RUDPPacketChannel channel, int size) { //return new byte[size]; Queue <byte[]> queue = _queues[(int)channel]; ReaderWriterLockSlim rwlock = _locks[(int)channel]; byte[] buffer; //---- Get from the cache rwlock.EnterWriteLock(); if (queue.Count > 0) { buffer = queue.Dequeue(); _currentAllocation -= buffer.Length; // Only usefull when MTU change if (buffer.Length != size) { Array.Resize <byte>(ref buffer, size); } rwlock.ExitWriteLock(); return(buffer); } rwlock.ExitWriteLock(); //---- Create new one if (_totalAllocation < MaxAllocation) { _totalAllocation += size; buffer = new byte[size]; // ask the garbage collector to avoid from relocating a certain object in memory System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned); GC.KeepAlive(buffer); return(buffer); } else { // Use the GC when we exceed the limit return(new byte[size]); } }
static public byte[] Allocate(RUDPPacketChannel channel, int size) { //return new byte[size]; Queue<byte[]> queue = _queues[(int)channel]; ReaderWriterLockSlim rwlock = _locks[(int)channel]; byte[] buffer; //---- Get from the cache rwlock.EnterWriteLock(); if (queue.Count > 0) { buffer = queue.Dequeue(); _currentAllocation -= buffer.Length; // Only usefull when MTU change if (buffer.Length != size) Array.Resize<byte>(ref buffer, size); rwlock.ExitWriteLock(); return buffer; } rwlock.ExitWriteLock(); //---- Create new one if (_totalAllocation < MaxAllocation) { _totalAllocation += size; buffer = new byte[size]; // ask the garbage collector to avoid from relocating a certain object in memory System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned); GC.KeepAlive(buffer); return buffer; } else // Use the GC when we exceed the limit return new byte[size]; }
static public void Deallocate(RUDPPacketChannel channel, byte[] buffer) { if (_currentAllocation < MaxAllocation) { Queue<byte[]> queue = _queues[(int)channel]; ReaderWriterLockSlim rwlock = _locks[(int)channel]; rwlock.EnterWriteLock(); queue.Enqueue(buffer); rwlock.ExitWriteLock(); _currentAllocation += buffer.Length; } }
/// <summary> /// Handle the bytes received from a socket. /// </summary> private static void HandlePayload(PhysicalSocket physical, byte[] payload, int length, IPEndPoint sender, out RUDPPacketChannel channel, out int packetId, out int advertisedWindowSize, out SACKSlot slot1, out SACKSlot slot2, out SACKSlot slot3, out SACKSlot slot4, out byte[] packetPayload) { int offset = 0; //-- Protocol version byte version = payload[offset]; offset++; //-- Header information byte sacksSlotCount = payload[offset]; offset++; //-- Channel byte channelByte = payload[offset]; offset++; switch (channelByte) { case 0: channel = RUDPPacketChannel.Undefined; break; case 10: channel = RUDPPacketChannel.Ping; break; case 20: channel = RUDPPacketChannel.PingRendezVous; break; case 30: channel = RUDPPacketChannel.TearDown; break; case 40: channel = RUDPPacketChannel.UserPacket; break; case 50: channel = RUDPPacketChannel.ACK; break; case 70: channel = RUDPPacketChannel.OutOfOrder; break; case 100: channel = RUDPPacketChannel.KeepAlive; break; case 120: channel = RUDPPacketChannel.MTUTuning; break; case 121: channel = RUDPPacketChannel.MTUTuningACK; break; case 150: channel = RUDPPacketChannel.Bandwidth01; break; case 151: channel = RUDPPacketChannel.BandwidthResponse01; break; case 155: channel = RUDPPacketChannel.Bandwidth02; break; case 156: channel = RUDPPacketChannel.BandwidthResponse02; break; default: throw new Exception("Unsupported channel type"); } //-- Packet Id packetId = BinaryHelper.ReadInt(payload, offset); offset += 4; //-- Control information : Advertised window advertisedWindowSize = BinaryHelper.ReadInt(payload, offset); offset += 4; //-- Payload length int payloadLength = BinaryHelper.ReadInt(payload, offset); offset += 4; //---- SACK Slots slot1 = null; slot2 = null; slot3 = null; slot4 = null; int startPacketId; int endPacketId; if (sacksSlotCount > 0) { startPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; endPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; slot1 = new SACKSlot(startPacketId, endPacketId); } else offset += 8; if (sacksSlotCount > 1) { startPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; endPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; slot2 = new SACKSlot(startPacketId, endPacketId); } else offset += 8; if (sacksSlotCount > 2) { startPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; endPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; slot3 = new SACKSlot(startPacketId, endPacketId); } else offset += 8; if (sacksSlotCount > 3) { startPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; endPacketId = BinaryHelper.ReadInt(payload, offset); offset += 4; slot4 = new SACKSlot(startPacketId, endPacketId); } else offset += 8; //-- Payload packetPayload = new byte[payloadLength]; if (payloadLength > 0) Buffer.BlockCopy(payload, offset, packetPayload, 0, payloadLength); }
/// <summary> /// Create the RUDP packet : HEADER + payload /// </summary> internal static byte[] MakePacketPayload(RUDPSocket rudp, int packetId, RUDPPacketChannel channel, SACKSlot slot1, SACKSlot slot2, SACKSlot slot3, SACKSlot slot4, byte[] payload, int offset, int payloadLength) { //---- Here we make the payload while we will send: /** * We create a header * The format is: * -------------- * - 1 byte : protocol version * - 1 byte : header information * - 1 byte : channel * - 4 bytes : message ID * - 4 bytes : advertized congestion window size * - 4 bytes : payloadLength * - 4 X (4 + 4) : 32 bytes for 4 SACK slots * - the payload bytes */ int headerOffset = 0; byte[] packetPayload = PayloadManager.Allocate(channel, RUDPHeaderLength + payloadLength); //---- Protocol version packetPayload[headerOffset] = (byte)1; headerOffset++; //---- Header information // 3 bits : number of ACK slots // sack slot (3 bits) byte sacksSlotCount = 0; if (slot1 != null) sacksSlotCount++; if (slot2 != null) sacksSlotCount++; if (slot3 != null) sacksSlotCount++; if (slot4 != null) sacksSlotCount++; packetPayload[headerOffset] = 0; // Reset (Because buffer reused) packetPayload[headerOffset] |= sacksSlotCount; headerOffset++; //---- Channel packetPayload[headerOffset] = (byte)channel; headerOffset++; //---- Message Id BinaryHelper.WriteInt(packetId, packetPayload, headerOffset); headerOffset += 4; //---- Control information : Advertised window if (rudp == null) BinaryHelper.WriteInt(-1, packetPayload, headerOffset); else BinaryHelper.WriteInt((int)rudp._controlWindow.AdvertisedWindow, packetPayload, headerOffset); headerOffset += 4; //---- Payload length BinaryHelper.WriteInt(payloadLength, packetPayload, headerOffset); headerOffset += 4; //---- SACK Slots if (slot1 != null) { BinaryHelper.WriteInt(slot1.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot1.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else { // Reset (Because buffer reused) Buffer.BlockCopy(Clean8Bytes, 0, packetPayload, headerOffset, 8); headerOffset += 8; } if (slot2 != null) { BinaryHelper.WriteInt(slot2.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot2.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else { // Reset (Because buffer reused) Buffer.BlockCopy(Clean8Bytes, 0, packetPayload, headerOffset, 8); headerOffset += 8; } if (slot3 != null) { BinaryHelper.WriteInt(slot3.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot3.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else { // Reset (Because buffer reused) Buffer.BlockCopy(Clean8Bytes, 0, packetPayload, headerOffset, 8); headerOffset += 8; } if (slot4 != null) { BinaryHelper.WriteInt(slot4.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot4.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else { // Reset (Because buffer reused) Buffer.BlockCopy(Clean8Bytes, 0, packetPayload, headerOffset, 8); headerOffset += 8; } if (payload == null) return packetPayload; //---- Payload Buffer.BlockCopy(payload, offset, packetPayload, headerOffset, payloadLength); return packetPayload; }
internal static bool PushPacketToSend(RUDPSocket rudp, bool reliablePacket, RUDPPacketChannel channel, byte[] payload, int offset, int payloadLength) { int packetId = -1; if (reliablePacket) packetId = Interlocked.Increment(ref rudp._ougoingPacketId); //---- Get the SACKs SACKSlot slot1 = null; SACKSlot slot2 = null; SACKSlot slot3 = null; SACKSlot slot4 = null; rudp._sackWindow.GetSLACKSlots(out slot1, out slot2, out slot3, out slot4); //---- Copy the payload to send byte[] rudpPayload = MakePacketPayload(rudp, packetId, channel, slot1, slot2, slot3, slot4, payload, offset, payloadLength); //---- Create a packet RUDPOutgoingPacket packet = NewOutgoingPacket(packetId, rudp._sequence, rudpPayload, channel); packet.CurrentCwnd = rudp._controlWindow.CWND; if (reliablePacket) { //---- Notify the control window rudp._controlWindow.OnSend(packetId, rudp._sequence, payloadLength); //---- Increment sequence number Interlocked.Exchange(ref rudp._sequence, rudp._sequence + payloadLength); } //---- In the "resend list" if (reliablePacket) { rudp._outgoingPacketsLock.EnterWriteLock(); rudp._outgoingPackets.Add(packet); rudp._outgoingPacketsLock.ExitWriteLock(); } //---- Send the packet packet.TSFirstSend = HiResTimer.MicroSeconds; if (!SocketSendPacket(rudp, packet, packet.Payload, packet.TSFirstSend)) { // Nothing to do... socket is closed and reseted ! return false; } return true; }
internal static RUDPOutgoingPacket NewOutgoingPacket(int packetId, long sequence, byte[] payload, RUDPPacketChannel channel) { //return new RUDPOutgoingPacket(packetId, sequence, payload, channel); RUDPOutgoingPacket packet; if (!_outgoingPacketsPools.TryDequeue(out packet)) { for (int index = 0; index < 100; index++) _outgoingPacketsPools.Enqueue(new RUDPOutgoingPacket(-1, -1, null, RUDPPacketChannel.Undefined)); return new RUDPOutgoingPacket(packetId, sequence, payload, channel); } packet.Reset(); packet.PacketId = packetId; packet.Payload = payload; packet.Channel = channel; packet.Sequence = sequence; return packet; }
private static RUDPSocket HandlePing(PhysicalSocket physical, IPEndPoint sender, int packetId, RUDPPacketChannel channel) { RUDPSocket rudp = null; physical._connectedRDUPsLock.EnterReadLock(); physical._connectedRDUPs.TryGetValue(sender, out rudp); physical._connectedRDUPsLock.ExitReadLock(); //---- Ping if (channel == RUDPPacketChannel.Ping) { //-- This connection already exist, duplicated Ping if (rudp != null) { // Resend the ACK rudp._sackWindow.OnReceivePacket(packetId); return null; } //-- No accepting socket if (physical._acceptingRDUP == null) { // Maybe the socket is not yet ready for accepting, do nothing return null; } //-- Accept rudp = physical.OnEndAccept(sender, packetId); //-- ACK connection rudp._sackWindow.OnReceivePacket(packetId); //return physical._acceptingRDUP; return rudp; } //---- Ping , with Rendez vous if (rudp != null && rudp._status == RUDPSocketStatus.Connecting && rudp._isRendezVousMode) { //---- End of connection rudp._status = RUDPSocketStatus.Connected; rudp.OnEndConnect(RUDPSocketError.Success); //---- Accept the rendez vous connection rudp._sackWindow.OnReceivePacket(packetId); return rudp; } return null; }
private static void HandlePacket(PhysicalSocket physical, IPEndPoint sender, RUDPPacketChannel channel, int packetId, int advertisedWindowSize, SACKSlot slot1, SACKSlot slot2, SACKSlot slot3, SACKSlot slot4, byte[] payload) { RUDPSocket rudp = null; //---- PING if (channel == RUDPPacketChannel.Ping || channel == RUDPPacketChannel.PingRendezVous) { rudp = HandlePing(physical, sender, packetId, channel); // Do not handle this message if (rudp == null) return; } //---- Search the socket if (rudp == null) { physical._connectedRDUPsLock.EnterReadLock(); physical._connectedRDUPs.TryGetValue(sender, out rudp); physical._connectedRDUPsLock.ExitReadLock(); } //---- Direct send of ACK, because socket can be shutdowned and removed if (channel == RUDPPacketChannel.TearDown) { byte[] packetPayload = MakePacketPayload(rudp, -1, RUDPPacketChannel.ACK, new SACKSlot(packetId, packetId), null, null, null, null, 0, 0); SocketSendACK(rudp, physical, sender, packetPayload); PayloadManager.Deallocate(RUDPPacketChannel.ACK, packetPayload); } //---- Released socket if (rudp == null) return; #if CONSOLE_TRACE if (packetId > -1) Trace("Handle packet (" + rudp.Handle + ")(" + channel + "):" + packetId); #endif //---- Advertised window rudp._controlWindow.OnReceiveAdvertisedWindow(advertisedWindowSize); //---- Handle ACKs HandleACKs(rudp, slot1, slot2, slot3, slot4); if (channel == RUDPPacketChannel.ACK) return; //---- Non reliable messages if (packetId < 0) { //-- Bandwidth if (channel == RUDPPacketChannel.Bandwidth01) { PushPacketToSend(rudp, false, RUDPPacketChannel.BandwidthResponse01, null, 0, 0); return; } else if (channel == RUDPPacketChannel.Bandwidth02) { PushPacketToSend(rudp, false, RUDPPacketChannel.BandwidthResponse02, payload, 0, 8); return; } else if (channel == RUDPPacketChannel.BandwidthResponse01) { rudp._bandwidthResponse01TS = HiResTimer.MicroSeconds; } else if (channel == RUDPPacketChannel.BandwidthResponse02) { //---- Calculate bandwidth // Bdw (Bytes / milli-sec) long now = HiResTimer.MicroSeconds; double delay = (now - rudp._bandwidthResponse01TS) / 1000; if (delay < 0.001) delay = 0.001; // Arrival Speed double arrivalSpeed = (RUDPHeaderLength + UDPHeaderLength) / delay; // RTT double currentRtt = (now - BinaryHelper.ReadInt(payload, 0)) / 1000; if (currentRtt < 0.001) currentRtt = 0.001; // BDP = Bandwidth(Byte / Ms) * RTT; double bandwidth = (long)(arrivalSpeed * currentRtt); rudp._bandwidth = (long)(rudp._bandwidth * 0.875f + bandwidth * 0.125f); } //-- MTU Tuning else if (channel == RUDPPacketChannel.MTUTuning) { rudp._pmtuDiscovery.OnReceiveProbe(payload.Length); return; } else if (channel == RUDPPacketChannel.MTUTuningACK) { rudp._pmtuDiscovery.OnReceiveProbeACK(payload); return; } //if ((rudp._incomingNonReliablePackets.Count * rudp._mtu) >= rudp._receiveSize) //return; RUDPIngoingPacket nonReliablePacket = new RUDPIngoingPacket(rudp, packetId, payload, channel, HiResTimer.MicroSeconds); rudp._incomingPackets.AddPacket(nonReliablePacket); rudp.HandleNextUserPacket(false); return; } //---- Do not process a duplicated packets bool isDuplicatedPacket; isDuplicatedPacket = (packetId <= rudp._incomingPackets.CurrentPacketId); if (!isDuplicatedPacket) isDuplicatedPacket = rudp._incomingPackets.ContainsPacket(packetId); //---- Can I receive the packet now ? bool canReceive = rudp._controlWindow.CanReceive(packetId, payload.Length); //---- Send the ACK if (channel != RUDPPacketChannel.Ping && channel != RUDPPacketChannel.PingRendezVous) if (canReceive || // Can receive, then we send ACK (!canReceive && isDuplicatedPacket)) // Is duplicated, then already in the list -> send another ACK rudp._sackWindow.OnReceivePacket(packetId); //---- Check if we can handle this message if (!canReceive || isDuplicatedPacket) return; //---- If we are not connected, we cannot hanlde messages ! We need a connection before. if (rudp._status != RUDPSocketStatus.Connected && channel == RUDPPacketChannel.UserPacket) return; //---- Check for Out of order packets if (rudp._incomingPackets.Count > 0) { int greaterPacketId = rudp._incomingPackets.LastPacketId; if (packetId != greaterPacketId + 1) { byte[] oooPayload = new byte[8]; // start packet BinaryHelper.WriteInt(greaterPacketId + 1, oooPayload, 0); // end packet BinaryHelper.WriteInt(packetId - 1, oooPayload, 4); PushPacketToSend(rudp, false, RUDPPacketChannel.OutOfOrder, oooPayload, 0, 8); } } //---- Receive Out of order notification if (channel == RUDPPacketChannel.OutOfOrder) { rudp._controlWindow.OnOutOfOrder(BinaryHelper.ReadInt(payload, 0), BinaryHelper.ReadInt(payload, 4)); } //---- TEAR DOWN if (channel == RUDPPacketChannel.TearDown) { // Initiate the close process if (rudp._status == RUDPSocketStatus.Connected) { // Notify control window rudp._controlWindow.OnReceive(null); // Start shutdown AsyncShutdown(rudp); } return; } //---- Add the packet to incoming list RUDPIngoingPacket packet = new RUDPIngoingPacket(rudp, packetId, payload, channel, HiResTimer.MicroSeconds); // Notify control window rudp._controlWindow.OnReceive(packet); rudp._incomingPackets.AddPacket(packet); //------ Handle the ordered ingoing packets rudp.HandleNextUserPacket(false); }