/// <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); }
static private void RegisterPhysicalSocket(PhysicalSocket physical) { EndPoint tempEndPoint = (EndPoint)physical._canReceiveFromEndPoint; physical._socket.BeginReceiveFrom(physical._receiveBuffer, 0, physical._receiveBuffer.Length, SocketFlags.None, ref tempEndPoint, new AsyncCallback(OnEndReceive), physical); }
private static bool SocketSendACK(RUDPSocket rudp, PhysicalSocket physical, IPEndPoint remoteEndPoint, byte[] rudpPayload) { try { physical._socket.SendTo(rudpPayload, remoteEndPoint); //physical._socket.BeginSendTo(rudpPayload, 0, rudpPayload.Length, SocketFlags.None, rudp._remoteEndPoint, null, null); } catch (SocketException exception) { if (rudp != null) OnSocketUnhandledError(rudp, SocketErrorToRUDPSocketError(exception.SocketErrorCode), null); return false; } if (rudp != null) rudp._lastACKSendTS = HiResTimer.MicroSeconds; return true; }
/// <summary> /// Static method to get/create a PhysicalSocket /// </summary> /// <param name="endPoint">The binded end point</param> internal static PhysicalSocket GetInstance(IPEndPoint endPoint) { lock (_physicals) { PhysicalSocket physical; bool isAnyEndPoint = endPoint.Equals(new IPEndPoint(IPAddress.Any, 0)); // Check if there is already an existing instance if (!isAnyEndPoint) { if (_physicals.TryGetValue(endPoint, out physical)) return physical; // In case no instance exists, create one physical = new PhysicalSocket(); physical.Bind(endPoint); RegisterPhysicalSocket(physical); _physicals.Add(endPoint, physical); } else { physical = new PhysicalSocket(); while (true) { int port = new Random().Next(Int16.MaxValue); IPAddress localAddress = LocalIPAddress(ProtocolType.IPv4); endPoint = new IPEndPoint(localAddress, port); // In case no instance exists, create one try { physical.Bind(endPoint); break; } catch { } } RegisterPhysicalSocket(physical); _physicals.Add(endPoint, physical); } return physical; } }
/// <summary> /// Release a PhysicalSocket instance and all its resources. /// </summary> /// <param name="physical">The socket to release</param> internal static void ReleaseInstance(PhysicalSocket physical) { lock (_physicals) { physical.Dispose(); _physicals.Remove((IPEndPoint)physical._socket.LocalEndPoint); } }
/// <summary> /// Called when we have an error on a socket. /// </summary> static internal void OnSocketUnhandledError(PhysicalSocket physical, IPEndPoint remoteEndPoint, SocketError error) { //---- Get the socket RUDPSocket rudp; physical._connectedRDUPsLock.EnterReadLock(); try { if (!physical._connectedRDUPs.TryGetValue(remoteEndPoint, out rudp)) return; // Released socket } finally { physical._connectedRDUPsLock.ExitReadLock(); } //---- Handle the error OnSocketUnhandledError(rudp, SocketErrorToRUDPSocketError(error), null); }
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); }
public void Bind(IPEndPoint endPoint) { // Get the associated physical socket _physical = RUDPStack.GetInstance(endPoint); }