/// <summary>
/// Holds the last ID allocated.
/// </summary>
- volatile int lastIDAllocated = ushort.MaxValue + 1;
+ private int lastIDAllocated = ushort.MaxValue + 1;
/// <summary>
/// The packets of data that have been transmitted reliably and not acknowledged.
/// The packet id that was received last.
/// </summary>
volatile ushort reliableReceiveLast = 0;
-
- public int DuplicateRecieves;
-
+
/// <summary>
/// Has the connection received anything yet
/// </summary>
/// 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 volatile float AveragePingMs = 500;
+ public float AveragePingMs = 500;
/// <summary>
/// The maximum times a message should be resent before marking the endpoint as disconnected.
private UdpConnection Connection;
private int Length;
- public volatile int NextTimeout;
+ public int NextTimeout;
public volatile bool Acknowledged;
public Action AckCallback;
- public volatile int Retransmissions;
+ public int Retransmissions;
public Stopwatch Stopwatch = new Stopwatch();
Packet()
{
if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
{
- connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {lifetime}ms");
+ connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {lifetime}ms ({self.Retransmissions} resends)");
self.Recycle();
}
{
if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
{
- connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {self.Retransmissions} resends");
+ connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {self.Retransmissions} resends ({lifetime}ms)");
self.Recycle();
}
}
catch (InvalidOperationException)
{
- //No longer connected
connection.Disconnect("Could not resend data as connection is no longer connected");
}
}
}
else
{
- Interlocked.Increment(ref this.DuplicateRecieves);
message.Recycle();
}
/// <inheritdoc />
public class UdpConnectionListener : NetworkConnectionListener
{
- public long BytesReceived;
- public long BytesSent;
-
- public const int BufferSize = ushort.MaxValue / 4;
+ public const int BufferSize = ushort.MaxValue;
public int MinConnectionLength = 0;
}
public float AveragePacketsTime = 1;
- public int PacketsResent = 0;
- public int KeepAlives = 0;
- public int DuplicateRecieves = 0;
Stopwatch stopwatch = new Stopwatch();
private void ManageReliablePackets(object state)
foreach (var kvp in this.allConnections)
{
var sock = kvp.Value;
- Interlocked.Add(ref PacketsResent, sock.ManageReliablePackets(state));
- KeepAlives += Interlocked.Exchange(ref sock.KeepAlivesSent, 0);
- DuplicateRecieves += Interlocked.Exchange(ref sock.DuplicateRecieves, 0);
+ sock.ManageReliablePackets(state);
}
this.AveragePacketsTime = this.AveragePacketsTime * .7f + stopwatch.ElapsedMilliseconds * .3f;
/// <summary>
/// Instructs the listener to begin listening.
/// </summary>
- void StartListeningForData()
+ public void StartListeningForData()
{
EndPoint remoteEP = EndPoint;
socket.BeginReceiveFrom(message.Buffer, 0, message.Buffer.Length, SocketFlags.None, ref remoteEP, ReadCallback, message);
Interlocked.Increment(ref ActiveListeners);
}
- catch (ObjectDisposedException)
- {
- return;
- }
catch (SocketException)
{
//Client no longer reachable, pretend it didn't happen
StartListeningForData();
return;
}
+ catch (Exception ex)
+ {
+ //If the socket's been disposed then we can just end there.
+ this.Logger?.Invoke("Stopped due to: " + ex.Message);
+ return;
+ }
}
/// <summary>
/// <param name="result">The asyncronous operation's result.</param>
public int ActiveListeners;
- public int ActiveCallbacks;
void ReadCallback(IAsyncResult result)
{
- Interlocked.Increment(ref ActiveCallbacks);
Interlocked.Decrement(ref ActiveListeners);
+ var message = (MessageReader)result.AsyncState;
+ int bytesReceived;
+ EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);
+
+ //End the receive operation
try
{
- var message = (MessageReader)result.AsyncState;
- int bytesReceived;
- EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);
+ bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint);
- //End the receive operation
- try
- {
- bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint);
- Interlocked.Add(ref BytesReceived, bytesReceived);
+ message.Offset = 0;
+ message.Length = bytesReceived;
+ }
+ catch (SocketException)
+ {
+ // Client no longer reachable, pretend it didn't happen
+ // TODO should this not inform the connection this client is lost???
- message.Offset = 0;
- message.Length = bytesReceived;
- }
- catch (NullReferenceException)
- {
- return;
- }
- catch (ObjectDisposedException)
- {
- //If the socket's been disposed then we can just end there.
- return;
- }
- catch (SocketException)
- {
- // Client no longer reachable, pretend it didn't happen
- // TODO should this not inform the connection this client is lost???
+ // This thread suggests the IP is not passed out from WinSoc so maybe not possible
+ // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
+ message.Recycle();
+
+ StartListeningForData();
+ return;
+ }
+ catch (Exception ex)
+ {
+ //If the socket's been disposed then we can just end there.
+ this.Logger?.Invoke("Stopped due to: " + ex.Message);
+ return;
+ }
- // This thread suggests the IP is not passed out from WinSoc so maybe not possible
- // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
- message.Recycle();
+ // Exit if no bytes read, we've closed.
+ if (bytesReceived == 0)
+ {
+ message.Recycle();
+ this.Logger?.Invoke("Stopped due to receiving 0 bytes");
+ return;
+ }
- UdpServerConnection dead;
- if (this.allConnections.TryRemove(remoteEndPoint, out dead))
- {
- dead.Dispose();
- }
+ //Begin receiving again
+ StartListeningForData();
- StartListeningForData();
- return;
- }
+ bool aware = true;
+ bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello;
+ bool isHello = hasHelloByte && message.Length >= MinConnectionLength;
- // Exit if no bytes read, we've closed.
- if (bytesReceived == 0)
+ //If we're aware of this connection use the one already
+ //If this is a new client then connect with them!
+ UdpServerConnection connection;
+ if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
+ {
+ //Check for malformed connection attempts
+ if (!isHello)
{
message.Recycle();
return;
}
- //Begin receiving again
- StartListeningForData();
-
- bool aware = true;
- bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello;
- bool isHello = hasHelloByte && message.Length >= MinConnectionLength;
-
- //If we're aware of this connection use the one already
- //If this is a new client then connect with them!
- UdpServerConnection connection;
- if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
+ lock (this.allConnections)
{
- //Check for malformed connection attempts
- if (!isHello)
- {
- message.Recycle();
- return;
- }
-
- lock (this.allConnections)
+ aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
+ if (!aware)
{
- aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
- if (!aware)
+ connection = new UdpServerConnection(this, remoteEndPoint, this.IPMode);
+ if (!this.allConnections.TryAdd(remoteEndPoint, connection))
{
- connection = new UdpServerConnection(this, remoteEndPoint, this.IPMode);
- if (!this.allConnections.TryAdd(remoteEndPoint, connection))
- {
- throw new Exception();
- }
+ throw new Exception();
}
}
}
+ }
- var stopwatch = System.Diagnostics.Stopwatch.StartNew();
- try
- {
- //Inform the connection of the buffer (new connections need to send an ack back to client)
- connection.HandleReceive(message, bytesReceived);
- }
- finally
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+ try
+ {
+ //Inform the connection of the buffer (new connections need to send an ack back to client)
+ connection.HandleReceive(message, bytesReceived);
+ }
+ finally
+ {
+ var el = stopwatch.ElapsedMilliseconds;
+ if (el > 5)
{
- var el = stopwatch.ElapsedMilliseconds;
- if (el > 5)
- {
- this.Logger?.Invoke($"Long Packet {el}ms = {string.Join(" ", message.Buffer.Take(bytesReceived))}");
- }
+ this.Logger?.Invoke($"Long Packet {el}ms = {string.Join(" ", message.Buffer.Take(bytesReceived))}");
}
+ }
- //If it's a new connection invoke the NewConnection event.
- if (!aware)
- {
- // Skip header and hello byte;
- message.Offset = 4;
- message.Length = bytesReceived - 4;
- message.Position = 0;
- InvokeNewConnection(message, connection);
- }
- else if (isHello || (!isHello && hasHelloByte))
- {
- message.Recycle();
- }
+ //If it's a new connection invoke the NewConnection event.
+ if (!aware)
+ {
+ // Skip header and hello byte;
+ message.Offset = 4;
+ message.Length = bytesReceived - 4;
+ message.Position = 0;
+ InvokeNewConnection(message, connection);
}
- finally
+ else if (isHello || (!isHello && hasHelloByte))
{
- Interlocked.Decrement(ref ActiveCallbacks);
+ message.Recycle();
}
}
+#if DEBUG
public int TestDropRate = -1;
private int dropCounter = 0;
+#endif
/// <summary>
/// Sends data from the listener socket.
internal void SendData(byte[] bytes, int length, EndPoint endPoint)
{
if (length > bytes.Length) return;
- Interlocked.Add(ref BytesSent, length);
+#if DEBUG
if (TestDropRate > 0)
{
if (Interlocked.Increment(ref dropCounter) % TestDropRate == 0)
return;
}
}
+#endif
try
{