/// <summary> /// Used when "resending" or sending queued "ACKs" /// </summary> internal List <SACKSlot> PrepareACKList() { List <SACKSlot> list = new List <SACKSlot>(); lock (this) { //---- Add all the slots, except the first one for (int index = 1; index < _slots.Count; index++) { SACKSlot slot = _slots[index]; if (slot.ACKsCount > 0) { slot.ACKsCount = 0; list.Add(slot.Clone()); } } //---- Add the first slot // Useful when some ACKs are loosed if (_slots[0].StartPacketId > -1) { if (list.Count > 1 || _slots[0].ACKsCount > 0) { _slots[0].ACKsCount = 0; list.Insert(0, _slots[0].Clone()); } } Interlocked.Exchange(ref _acksCount, 0); } CheckACKCount(); return(list); }
internal SACKWindow() { SACKSlot slot = new SACKSlot(-1, -1); slot.ACKsCount = 0; _slots.Add(slot); }
/// <summary> /// Used when sending a packet. /// </summary> internal void GetSLACKSlots(out SACKSlot slot1, out SACKSlot slot2, out SACKSlot slot3, out SACKSlot slot4) { slot1 = null; slot2 = null; slot3 = null; slot4 = null; lock (this) { if (_slots[0].StartPacketId < 0) { return; } //---- Useful when some ACKs are loosed slot1 = _slots[0].Clone(); _slots[0].ACKsCount = 0; Interlocked.Exchange(ref _acksCount, _acksCount - slot1.ACKsCount); CheckACKCount(); //---- Add all the slots for (int index = 1; index < _slots.Count; index++) { SACKSlot slot = _slots[index]; if (slot.ACKsCount > 0) { Interlocked.Exchange(ref _acksCount, _acksCount - slot.ACKsCount); slot.ACKsCount = 0; CheckACKCount(); if (slot2 == null) { slot2 = slot.Clone(); } else if (slot3 == null) { slot3 = slot.Clone(); } else if (slot4 == null) { slot4 = slot.Clone(); return; } } } } }
internal void OnReceivePacket(int packetId) { RUDPStack.Trace("SACK OnReceivePacket:" + packetId); Monitor.Enter(this); try { SACKSlot slot; SACKSlot newSlot; for (int index = 0; index < _slots.Count; index++) { slot = _slots[index]; //-- Already in the slot if (packetId >= slot.StartPacketId && packetId <= slot.EndPacketId) { slot.ACKsCount++; return; } //-- Grow the slot if (slot.EndPacketId + 1 == packetId) { slot.EndPacketId++; slot.ACKsCount++; // First packet if (slot.StartPacketId < 0) { slot.StartPacketId = 0; } // Merge with next if ((index + 1) < _slots.Count && (slot.EndPacketId + 1) == _slots[index + 1].StartPacketId) { index++; slot.EndPacketId = _slots[index].EndPacketId; slot.ACKsCount += _slots[index].ACKsCount; _slots.RemoveAt(index); } return; } //-- Before this slot (and then after previous one !) if (packetId < slot.StartPacketId) { if (packetId == slot.StartPacketId - 1) { slot.StartPacketId--; slot.ACKsCount++; } else { newSlot = new SACKSlot(packetId, packetId); _slots.Insert(index, newSlot); } return; } } //-- New slot at the end newSlot = new SACKSlot(packetId, packetId); _slots.Add(newSlot); } finally { Interlocked.Increment(ref _acksCount); Monitor.Exit(this); CheckACKCount(); } }
/// <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; }
static private void UpdatePacketPayload(byte[] packetPayload, SACKSlot slot1, SACKSlot slot2, SACKSlot slot3, SACKSlot slot4) { //---- Update header byte sacksSlotCount = 0; if (slot1 != null) sacksSlotCount++; if (slot2 != null) sacksSlotCount++; if (slot3 != null) sacksSlotCount++; if (slot4 != null) sacksSlotCount++; packetPayload[1] = sacksSlotCount; //---- Update slots int headerOffset = 15; if (slot1 != null) { BinaryHelper.WriteInt(slot1.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot1.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else headerOffset += 8; if (slot2 != null) { BinaryHelper.WriteInt(slot2.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot2.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else headerOffset += 8; if (slot3 != null) { BinaryHelper.WriteInt(slot3.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot3.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } else headerOffset += 8; if (slot4 != null) { BinaryHelper.WriteInt(slot4.StartPacketId, packetPayload, headerOffset); headerOffset += 4; BinaryHelper.WriteInt(slot4.EndPacketId, packetPayload, headerOffset); headerOffset += 4; } //else headerOffset += 8; }
private static void HandleACKs(RUDPSocket rudp, SACKSlot slot1, SACKSlot slot2, SACKSlot slot3, SACKSlot slot4) { // No ack if (slot1 == null) return; int maxId = slot1.EndPacketId; if (slot4 != null) maxId = slot4.EndPacketId; else if (slot3 != null) maxId = slot3.EndPacketId; else if (slot2 != null) maxId = slot2.EndPacketId; #if CONSOLE_TRACE if (slot1 != null) Trace("Handle ACK[1](" + rudp.Handle + "): " + slot1.StartPacketId + " <-> " + slot1.EndPacketId); if (slot2 != null) Trace("Handle ACK[2](" + rudp.Handle + "): " + slot2.StartPacketId + " <-> " + slot2.EndPacketId); if (slot3 != null) Trace("Handle ACK[3](" + rudp.Handle + "): " + slot3.StartPacketId + " <-> " + slot3.EndPacketId); if (slot4 != null) Trace("Handle ACK[4](" + rudp.Handle + "): " + slot4.StartPacketId + " <-> " + slot4.EndPacketId); #endif //---- Prepare the list of packets List<RUDPOutgoingPacket> toACKPackets = new List<RUDPOutgoingPacket>(); RUDPOutgoingPacket lastPacket = null; double currentRTT = Double.MaxValue; rudp._outgoingPacketsLock.EnterReadLock(); try { for (int index = 0; index < rudp._outgoingPackets.Count; index++) { RUDPOutgoingPacket packet = rudp._outgoingPackets[index]; if (packet.PacketId > maxId) break; if (packet.IsACKed) continue; if (slot4 != null) if (packet.PacketId >= slot4.StartPacketId && packet.PacketId <= slot4.EndPacketId) { if (packet.Retransmission < 1) { lastPacket = packet; currentRTT = Math.Min(currentRTT, HiResTimer.MicroSeconds - lastPacket.TSFirstSend); } toACKPackets.Add(packet); continue; } if (slot3 != null) if (packet.PacketId >= slot3.StartPacketId && packet.PacketId <= slot3.EndPacketId) { if (packet.Retransmission < 1) { lastPacket = packet; currentRTT = Math.Min(currentRTT, HiResTimer.MicroSeconds - lastPacket.TSFirstSend); } toACKPackets.Add(packet); continue; } if (slot2 != null) if (packet.PacketId >= slot2.StartPacketId && packet.PacketId <= slot2.EndPacketId) { if (packet.Retransmission < 1) { lastPacket = packet; currentRTT = Math.Min(currentRTT, HiResTimer.MicroSeconds - lastPacket.TSFirstSend); } toACKPackets.Add(packet); continue; } if (packet.PacketId >= slot1.StartPacketId && packet.PacketId <= slot1.EndPacketId) { if (packet.Retransmission < 1) { lastPacket = packet; currentRTT = Math.Min(currentRTT, HiResTimer.MicroSeconds - lastPacket.TSFirstSend); } toACKPackets.Add(packet); } } } finally { rudp._outgoingPacketsLock.ExitReadLock(); } //---- If no good packet, use current RTT if (lastPacket == null) currentRTT = rudp.RTT; if (currentRTT < 1) currentRTT = 1; //---- Set the ACK for all the packets for (int index = 0; index < toACKPackets.Count; index++) { RUDPOutgoingPacket packet = toACKPackets[index]; SetPacketACKed(rudp, packet, currentRTT); } }
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); }
internal void OnReceivePacket(int packetId) { RUDPStack.Trace("SACK OnReceivePacket:" + packetId); Monitor.Enter(this); try { SACKSlot slot; SACKSlot newSlot; for (int index = 0; index < _slots.Count; index++) { slot = _slots[index]; //-- Already in the slot if (packetId >= slot.StartPacketId && packetId <= slot.EndPacketId) { slot.ACKsCount++; return; } //-- Grow the slot if (slot.EndPacketId + 1 == packetId) { slot.EndPacketId++; slot.ACKsCount++; // First packet if (slot.StartPacketId < 0) slot.StartPacketId = 0; // Merge with next if ((index + 1) < _slots.Count && (slot.EndPacketId + 1) == _slots[index + 1].StartPacketId) { index++; slot.EndPacketId = _slots[index].EndPacketId; slot.ACKsCount += _slots[index].ACKsCount; _slots.RemoveAt(index); } return; } //-- Before this slot (and then after previous one !) if (packetId < slot.StartPacketId) { if (packetId == slot.StartPacketId - 1) { slot.StartPacketId--; slot.ACKsCount++; } else { newSlot = new SACKSlot(packetId, packetId); _slots.Insert(index, newSlot); } return; } } //-- New slot at the end newSlot = new SACKSlot(packetId, packetId); _slots.Add(newSlot); } finally { Interlocked.Increment(ref _acksCount); Monitor.Exit(this); CheckACKCount(); } }
/// <summary> /// Used when sending a packet. /// </summary> internal void GetSLACKSlots(out SACKSlot slot1, out SACKSlot slot2, out SACKSlot slot3, out SACKSlot slot4) { slot1 = null; slot2 = null; slot3 = null; slot4 = null; lock (this) { if (_slots[0].StartPacketId < 0) return; //---- Useful when some ACKs are loosed slot1 = _slots[0].Clone(); _slots[0].ACKsCount = 0; Interlocked.Exchange(ref _acksCount, _acksCount - slot1.ACKsCount); CheckACKCount(); //---- Add all the slots for (int index = 1; index < _slots.Count; index++) { SACKSlot slot = _slots[index]; if (slot.ACKsCount > 0) { Interlocked.Exchange(ref _acksCount, _acksCount - slot.ACKsCount); slot.ACKsCount = 0; CheckACKCount(); if (slot2 == null) slot2 = slot.Clone(); else if (slot3 == null) slot3 = slot.Clone(); else if (slot4 == null) { slot4 = slot.Clone(); return; } } } } }