/// <summary> /// Reads a packet from a stream with the given size in bytes or returns null if the packet cannot be parsed. /// </summary> public static Packet Read(InStream Stream, int Size) { Packet packet = new Packet(); // Read flags and header if ((Size -= StreamSize.Byte + StreamSize.Int) < 0) return null; PacketFlags flags = (PacketFlags)Stream.ReadByte(); packet.SequenceNumber = Stream.ReadInt(); packet.PingRequest = (flags & PacketFlags.PingRequest) == PacketFlags.PingRequest; packet.PingResponse = (flags & PacketFlags.PingResponse) == PacketFlags.PingResponse; // Read additional information if ((flags & PacketFlags.Acknowledgement) == PacketFlags.Acknowledgement) { if ((Size -= StreamSize.Int) < 0) return null; packet.AcknowledgementNumber = Stream.ReadInt(); } if ((flags & PacketFlags.RoundTripTime) == PacketFlags.RoundTripTime) { if ((Size -= StreamSize.Double) < 0) return null; packet.RoundTripTime = Stream.ReadDouble(); } // Read chunk if any if ((flags & PacketFlags.Chunk) == PacketFlags.Chunk) { packet.ChunkInitial = (flags & PacketFlags.ChunkInitial) == PacketFlags.ChunkInitial; packet.ChunkFinal = (flags & PacketFlags.ChunkFinal) == PacketFlags.ChunkFinal; byte[] data = new byte[Size]; Stream.Read(data, 0, data.Length); packet.ChunkData = data; return packet; } else { // A packet can only be a disconnect if it does not have a chunk packet.Disconnect = (flags & PacketFlags.Disconnect) == PacketFlags.Disconnect; // Make sure this is the end of the packet if (Size == 0) return packet; else return null; } }
/// <summary> /// Writes a packet to a stream. /// </summary> public static void Write(Packet Packet, OutStream Stream) { // Build flags and header PacketFlags flags = PacketFlags.Empty; if (Packet.AcknowledgementNumber.HasValue) flags |= PacketFlags.Acknowledgement; if (Packet.RoundTripTime.HasValue) flags |= PacketFlags.RoundTripTime; if (Packet.ChunkData != null) { flags |= PacketFlags.Chunk; if (Packet.ChunkInitial) flags |= PacketFlags.ChunkInitial; if (Packet.ChunkFinal) flags |= PacketFlags.ChunkFinal; } if (Packet.PingRequest) flags |= PacketFlags.PingRequest; if (Packet.PingResponse) flags |= PacketFlags.PingResponse; if (Packet.Disconnect) flags |= PacketFlags.Disconnect; Stream.WriteByte((byte)flags); Stream.WriteInt(Packet.SequenceNumber); // Additional information int ack; if (Packet.AcknowledgementNumber.TryGetValue(out ack)) Stream.WriteInt(ack); double rtt; if (Packet.RoundTripTime.TryGetValue(out rtt)) Stream.WriteDouble(rtt); // Chunk if (Packet.ChunkData != null) Stream.Write(Packet.ChunkData, 0, Packet.ChunkData.Length); }
/// <summary> /// Immediately sends a ping response packet of some sort. /// </summary> private void _SendPingResponse(UDPHub Hub) { byte[] data = null; bool initial = false; bool final = false; int sequencenumber = 0; Packet packet; if(this._OutTerminal.Process(ref sequencenumber, ref data, ref initial, ref final)) { packet = new Packet { SequenceNumber = sequencenumber, ChunkData = data, ChunkInitial = initial, ChunkFinal = final }; } else { packet = new Packet { SequenceNumber = this._OutTerminal.SendNumber }; } packet.PingResponse = true; Hub.Send(packet, this.EndPoint); }
/// <summary> /// Fills the given packet with additional information. /// </summary> private void _FillAdditional(Packet Packet) { if (this._ShouldSendAcknowledgement) { this._ShouldSendAcknowledgement = false; Packet.AcknowledgementNumber = this.InTerminal.AcknowledgementNumber; } if (this._ShouldSendRoundTripTime) { this._ShouldSendRoundTripTime = false; Packet.RoundTripTime = this.RoundTripTime; } }
/// <summary> /// Updates the state of the peer and sends packets if needed. /// </summary> internal void _Update(UDPHub Hub, double Time, out bool Remove) { UDPHubSettings settings = this._Settings; OutTerminal oterm = this._OutTerminal; // Handle waves this._WaveDelay -= Time; if (this._WaveDelay <= 0.0) { // Check acknowledgement and adjust wave size if (oterm.AcknowledgementNumber == oterm.SendNumber) { if (this._FullWave) { this._WaveSize++; this._FullWave = false; } } else { oterm.SendNumber = oterm.AcknowledgementNumber; if (this._WaveSize > 1) { this._WaveSize--; } } int chunksequencenumber = 0; byte[] chunkdata = null; bool chunkinitial = false; bool chunkfinal = false; int wavesize = 0; if (oterm.Process(ref chunksequencenumber, ref chunkdata, ref chunkinitial, ref chunkfinal)) { while (true) { // Send chunk wavesize++; Packet chunk = new Packet { SequenceNumber = chunksequencenumber, ChunkData = chunkdata, ChunkInitial = chunkinitial, ChunkFinal = chunkfinal }; this._FillAdditional(chunk); Hub.Send(chunk, this._EndPoint); // Check if wave is full if (wavesize == this._WaveSize) { this._FullWave = true; break; } // Get next chunk if (!oterm.Process(ref chunksequencenumber, ref chunkdata, ref chunkinitial, ref chunkfinal)) { break; } } // Reset delays this._WaveDelay = this._RoundTripTime + settings.WaveRate; this._KeepAliveDelay = settings.KeepAliveDelay; } } // Send keep alive if needed this._KeepAliveDelay -= Time; if (this._KeepAliveDelay <= 0.0) { Packet packet = new Packet { SequenceNumber = this._OutTerminal.SendNumber }; this._FillAdditional(packet); Hub.Send(packet, this.EndPoint); this._KeepAliveDelay = settings.KeepAliveDelay; } // Check for implicit disconnect Remove = false; this._ExpireDelay -= Time; if (this._ExpireDelay <= 0.0) { Remove = true; } }
/// <summary> /// Called when an (unvalidated) packet is received for this peer. /// </summary> internal void _Receive(UDPHubSettings Settings, UDPHub Hub, Packet Packet, out bool Remove) { Remove = false; // Validate if (!this._Valid(Packet.SequenceNumber)) return; // Disconnect? if (Packet.Disconnect) { Remove = true; return; } // Reset expire delay this._ExpireDelay = Settings.ExpireDelay; // Read additional information int ack; if (Packet.AcknowledgementNumber.TryGetValue(out ack)) { this._OutTerminal.AcknowledgementNumber = ack; } double rtt; if (Packet.RoundTripTime.TryGetValue(out rtt)) { this._RoundTripTime = rtt; } // Chunk? if (Packet.ChunkData != null) { this._Process(Packet.SequenceNumber, Packet.ChunkData, Packet.ChunkInitial, Packet.ChunkFinal); } // Ping? if (Packet.PingRequest) { this._SendPingResponse(Hub); } }
/// <summary> /// Uses the connection and settings of this hub to send a packet to the given endpoint. /// </summary> public void Send(Packet Packet, IPEndPoint To) { BufferOutStream bos = new BufferOutStream(this._Settings.SendBuffer, 0); Packet.Write(Packet, bos); this._UDP.Send(To, Data.FromBuffer(bos.Buffer, 0, bos.Position)); }