/// The starting timeout, in miliseconds, at which data will be resent.
/// </summary>
/// <remarks>
- /// For reliable delivery data is resent at specified intervals unless an acknowledgement is received from the
- /// receiving device. The ResendTimeout specifies the interval between the packets being resent, each time a packet
- /// is resent the interval is doubled for that packet until the number of resends exceeds the
- /// <see cref="ResendsBeforeDisconnect"/> value.
+ /// <para>
+ /// For reliable delivery data is resent at specified intervals unless an acknowledgement is received from the
+ /// receiving device. The ResendTimeout specifies the interval between the packets being resent, each time a packet
+ /// is resent the interval is doubled for that packet until the number of resends exceeds the
+ /// <see cref="ResendsBeforeDisconnect"/> value.
+ /// </para>
+ /// <para>
+ /// Setting this to its default of 0 will mean the timout is 4 times the value of the average ping, usually
+ /// resulting in a more dynamic resend that responds to endpoints on slower or faster connections.
+ /// </para>
/// </remarks>
public int ResendTimeout { get { return resendTimeout; } set { resendTimeout = value; } }
- private volatile int resendTimeout = 200; //TODO this based of average ping?
+ private volatile int resendTimeout = 200;
/// <summary>
/// Holds the last ID allocated.
/// </summary>
volatile bool hasReceivedSomething = false;
+ /// <summary>
+ /// The total time it has taken reliable packets to make a round trip.
+ /// </summary>
+ long totalRoundTime = 0;
+
+ /// <summary>
+ /// The number of reliable messages that have been sent.
+ /// </summary>
+ long totalReliableMessages = 0;
+
+ /// <summary>
+ /// Returns the average ping to this endpoint.
+ /// </summary>
+ /// <remarks>
+ /// This returns the average ping for a one-way trip as calculated from the reliable packets that have been sent
+ /// and acknowledged by the endpoint.
+ /// </remarks>
+ public double AveragePing
+ {
+ get
+ {
+ long t = Interlocked.Read(ref totalReliableMessages);
+ if (t == 0)
+ return 0;
+ else
+ return Interlocked.Read(ref totalRoundTime) / t / 2;
+ }
+ }
+
/// <summary>
/// The maximum times a message should be resent before marking the endpoint as disconnected.
/// </summary>
public Action AckCallback;
public volatile bool Acknowledged;
public volatile int Retransmissions;
+ public Stopwatch Stopwatch = new Stopwatch();
Packet()
{
AckCallback = ackCallback;
Acknowledged = false;
Retransmissions = 0;
+
+ Stopwatch.Reset();
+ Stopwatch.Start();
}
/// <summary>
if (++p.Retransmissions > ResendsBeforeDisconnect)
{
HandleDisconnect();
+
+ //Set acknowledged so we dont change the timer again
+ p.Acknowledged = true;
+
p.Recycle();
return;
}
}
}
- WriteBytesToConnection(p.Data);
+ try
+ {
+ WriteBytesToConnection(p.Data);
+ }
+ catch (InvalidOperationException e)
+ {
+ //No longer connected
+ HandleDisconnect(new HazelException("Could not resend data as connection is no longer connected", e));
+ }
Trace.WriteLine("Resend.");
},
- resendTimeout,
+ resendTimeout > 0 ? resendTimeout : (AveragePing != 0 ? (int)AveragePing * 4 : 200),
ackCallback
);
if (packet.AckCallback != null)
packet.AckCallback.Invoke();
+ //Add to average ping
+ packet.Stopwatch.Stop();
+ Interlocked.Add(ref totalRoundTime, packet.Stopwatch.Elapsed.Milliseconds);
+ Interlocked.Increment(ref totalReliableMessages);
+
packet.Recycle();
reliableDataPacketsSent.Remove(id);
/// </summary>
Socket listener;
+ IPEndPoint bindOn;
+
/// <summary>
/// Buffer to store incoming data in.
/// </summary>
this.IPAddress = IPAddress;
this.Port = port;
+ this.bindOn = new IPEndPoint(IPAddress, Port);
+
if (mode == IPMode.IPv4)
this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
else
try
{
lock (listener)
- listener.Bind(new IPEndPoint(IPAddress, Port));
+ listener.Bind(bindOn);
}
catch (SocketException e)
{
/// </summary>
void StartListeningForData()
{
- EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
+ EndPoint remoteEP = (EndPoint)bindOn;
try
{
connection = new UdpServerConnection(this, remoteEndPoint);
connections.Add(remoteEndPoint, connection);
-
- //Then ping back an ack to make sure they're happy
+
+ //Then ping back an ack to make sure they're happy (unless we rejected them...)
connection.SendAck(buffer[1], buffer[2]);
}
}