Skip to content

Commit

Permalink
refactor(SocketLayer)!: moving MaxPacketSize to SocketFactory
Browse files Browse the repository at this point in the history
Allows max packet size to be set by sockets rather than the general peer config.

BREAKING CHANGE: socket factories now have to override MaxPacketSize property
  • Loading branch information
James-Frowen committed Mar 20, 2022
1 parent fefdfaa commit 49c7f41
Show file tree
Hide file tree
Showing 23 changed files with 90 additions and 60 deletions.
5 changes: 3 additions & 2 deletions Assets/Mirage/Runtime/NetworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,16 @@ public void Connect(string address = null, ushort? port = null)
if (logger.LogEnabled()) logger.Log($"Client connecting to endpoint: {endPoint}");

ISocket socket = SocketFactory.CreateClientSocket();
int maxPacketSize = SocketFactory.MaxPacketSize;
MessageHandler = new MessageHandler(World, DisconnectOnException);
var dataHandler = new DataHandler(MessageHandler);
Metrics = EnablePeerMetrics ? new Metrics(MetricsSize) : null;

Config config = PeerConfig ?? new Config();

NetworkWriterPool.Configure(config.MaxPacketSize);
NetworkWriterPool.Configure(maxPacketSize);

peer = new Peer(socket, dataHandler, config, LogFactory.GetLogger<Peer>(), Metrics);
peer = new Peer(socket, maxPacketSize, dataHandler, config, LogFactory.GetLogger<Peer>(), Metrics);
peer.OnConnected += Peer_OnConnected;
peer.OnConnectionFailed += Peer_OnConnectionFailed;
peer.OnDisconnected += Peer_OnDisconnected;
Expand Down
5 changes: 3 additions & 2 deletions Assets/Mirage/Runtime/NetworkServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ public void StartServer(NetworkClient localClient = null)
};
}

NetworkWriterPool.Configure(config.MaxPacketSize);
int maxPacketSize = SocketFactory.MaxPacketSize;
NetworkWriterPool.Configure(maxPacketSize);

// Are we listening for incoming connections?
// If yes, set up a socket for incoming connections (we're a multiplayer game).
Expand All @@ -222,7 +223,7 @@ public void StartServer(NetworkClient localClient = null)
ISocket socket = SocketFactory.CreateServerSocket();

// Tell the peer to use that newly created socket.
peer = new Peer(socket, dataHandler, config, LogFactory.GetLogger<Peer>(), Metrics);
peer = new Peer(socket, maxPacketSize, dataHandler, config, LogFactory.GetLogger<Peer>(), Metrics);
peer.OnConnected += Peer_OnConnected;
peer.OnDisconnected += Peer_OnDisconnected;
// Bind it to the endpoint.
Expand Down
4 changes: 2 additions & 2 deletions Assets/Mirage/Runtime/Serialization/NetworkWriterPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public static class NetworkWriterPool
static NetworkWriterPool()
{
// auto configure so that pool can be used without having to manually call it
var config = new Config();
Configure(config.MaxPacketSize);
// 1300 is greater than udp's MTU value
Configure(1300);
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions Assets/Mirage/Runtime/SocketLayer/AckSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ internal class AckSystem
/// <param name="connection"></param>
/// <param name="ackTimeout">how long after last send before sending empty ack</param>
/// <param name="time"></param>
public AckSystem(IRawConnection connection, Config config, ITime time, Pool<ByteBuffer> bufferPool, Metrics metrics = null)
public AckSystem(IRawConnection connection, Config config, int maxPacketSize, ITime time, Pool<ByteBuffer> bufferPool, Metrics metrics = null)
{
if (config == null) throw new ArgumentNullException(nameof(config));

Expand All @@ -90,7 +90,7 @@ public AckSystem(IRawConnection connection, Config config, ITime time, Pool<Byte
ackTimeout = config.TimeBeforeEmptyAck;
emptyAckLimit = config.EmptyAckLimit;
receivesBeforeEmpty = config.ReceivesBeforeEmptyAck;
maxPacketSize = config.MaxPacketSize;
this.maxPacketSize = maxPacketSize;
maxPacketsInSendBufferPerConnection = config.MaxReliablePacketsInSendBufferPerConnection;

maxFragments = config.MaxReliableFragments;
Expand Down
20 changes: 1 addition & 19 deletions Assets/Mirage/Runtime/SocketLayer/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,6 @@ public class Config
/// </summary>
public float DisconnectDuration = 1;

/// <summary>
/// IPv6 + UDP Header
/// </summary>
const int HEADER_SIZE = 40 + 8;

/// <summary>
/// MTU is expected to be atleast this number
/// </summary>
const int MIN_MTU = 1280;

/// <summary>
/// Max size of array that will be sent to or can be received from <see cref="ISocket"/>
/// <para>This will also be the size of all buffers used by <see cref="Peer"/></para>
/// <para>This is not max message size because this size includes packets header added by <see cref="Peer"/></para>
/// </summary>
// todo move these settings to socket
public int MaxPacketSize = MIN_MTU - HEADER_SIZE;

/// <summary>
/// How many buffers to create at start
/// </summary>
Expand Down Expand Up @@ -111,7 +93,7 @@ public class Config

/// <summary>
/// How many fragments large reliable message can be split into
/// <para>if set to 0 then messages over <see cref="MaxPacketSize"/> will not be allowed to be sent</para>
/// <para>if set to 0 then messages over <see cref="SocketFactory.MaxPacketSize"/> will not be allowed to be sent</para>
/// <para>max value is 255</para>
/// </summary>
public int MaxReliableFragments = 5;
Expand Down
4 changes: 2 additions & 2 deletions Assets/Mirage/Runtime/SocketLayer/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public ConnectionState State

IEndPoint IConnection.EndPoint => EndPoint;

internal Connection(Peer peer, IEndPoint endPoint, IDataHandler dataHandler, Config config, Time time, Pool<ByteBuffer> bufferPool, ILogger logger, Metrics metrics)
internal Connection(Peer peer, IEndPoint endPoint, IDataHandler dataHandler, Config config, int maxPacketSize, Time time, Pool<ByteBuffer> bufferPool, ILogger logger, Metrics metrics)
{
this.peer = peer;
this.logger = logger;
Expand All @@ -124,7 +124,7 @@ internal Connection(Peer peer, IEndPoint endPoint, IDataHandler dataHandler, Con
disconnectedTracker = new DisconnectedTracker(config, time);

this.metrics = metrics;
ackSystem = new AckSystem(this, config, time, bufferPool, metrics);
ackSystem = new AckSystem(this, config, maxPacketSize, time, bufferPool, metrics);
}

public override string ToString()
Expand Down
14 changes: 9 additions & 5 deletions Assets/Mirage/Runtime/SocketLayer/Peer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public sealed class Peer : IPeer
readonly ISocket socket;
readonly IDataHandler dataHandler;
readonly Config config;
readonly int maxPacketSize;
readonly Time time;

readonly ConnectKeyValidator connectKeyValidator;
Expand All @@ -59,19 +60,22 @@ public sealed class Peer : IPeer
/// </summary>
bool active;

public Peer(ISocket socket, IDataHandler dataHandler, Config config = null, ILogger logger = null, Metrics metrics = null)
public Peer(ISocket socket, int maxPacketSize, IDataHandler dataHandler, Config config = null, ILogger logger = null, Metrics metrics = null)
{
this.logger = logger;
this.metrics = metrics;
this.config = config ?? new Config();
this.maxPacketSize = maxPacketSize;
if (maxPacketSize < AckSystem.MIN_RELIABLE_HEADER_SIZE + 1)
throw new ArgumentException($"Max packet size too small for AckSystem header", nameof(maxPacketSize));

this.socket = socket ?? throw new ArgumentNullException(nameof(socket));
this.dataHandler = dataHandler ?? throw new ArgumentNullException(nameof(dataHandler));
time = new Time();

connectKeyValidator = new ConnectKeyValidator(this.config.key);

bufferPool = new Pool<ByteBuffer>(ByteBuffer.CreateNew, this.config.MaxPacketSize, this.config.BufferPoolStartSize, this.config.BufferPoolMaxSize, this.logger);
bufferPool = new Pool<ByteBuffer>(ByteBuffer.CreateNew, maxPacketSize, this.config.BufferPoolStartSize, this.config.BufferPoolMaxSize, this.logger);
Application.quitting += Application_quitting;
}

Expand Down Expand Up @@ -253,8 +257,8 @@ private void ReceiveLoop()
int length = socket.Receive(buffer.array, out IEndPoint receiveEndPoint);

// this should never happen. buffer size is only MTU, if socket returns higher length then it has a bug.
if (length > config.MaxPacketSize)
throw new IndexOutOfRangeException($"Socket returned length above MTU. MaxPacketSize:{config.MaxPacketSize} length:{length}");
if (length > maxPacketSize)
throw new IndexOutOfRangeException($"Socket returned length above MTU. MaxPacketSize:{maxPacketSize} length:{length}");

var packet = new Packet(buffer, length);

Expand Down Expand Up @@ -432,7 +436,7 @@ private Connection CreateNewConnection(IEndPoint _newEndPoint)
// this is so that we can re-use the endpoint (reduces alloc) for receive and not worry about changing internal data needed for each connection
IEndPoint endPoint = _newEndPoint?.CreateCopy();

var connection = new Connection(this, endPoint, dataHandler, config, time, bufferPool, logger, metrics);
var connection = new Connection(this, endPoint, dataHandler, config, maxPacketSize, time, bufferPool, logger, metrics);
connection.SetReceiveTime();
connections.Add(endPoint, connection);
return connection;
Expand Down
4 changes: 4 additions & 0 deletions Assets/Mirage/Runtime/SocketLayer/SocketFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public interface IHasPort
/// </remarks>
public abstract class SocketFactory : MonoBehaviour
{
/// <summary>Max size for packets sent to or received from Socket
/// <para>Called once when Sockets are created</para></summary>
public abstract int MaxPacketSize { get; }

/// <summary>Creates a <see cref="ISocket"/> to be used by <see cref="Peer"/> on the server</summary>
/// <exception cref="NotSupportedException">Throw when Server is not supported on current platform</exception>
public abstract ISocket CreateServerSocket();
Expand Down
23 changes: 23 additions & 0 deletions Assets/Mirage/Runtime/Sockets/Udp/UdpSocketFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public sealed class UdpSocketFactory : SocketFactory, IHasAddress, IHasPort
[Header("NanoSocket options")]
public int BufferSize = 256 * 1024;

public override int MaxPacketSize => UdpMTU.MaxPacketSize;

bool useNanoSocket => SocketLib == SocketLib.Native || (SocketLib == SocketLib.Automatic && IsDesktop);

string IHasAddress.Address
Expand Down Expand Up @@ -178,4 +180,25 @@ IEndPoint IEndPoint.CreateCopy()
return new EndPointWrapper(copy);
}
}

public class UdpMTU
{
/// <summary>
/// IPv6 + UDP Header
/// </summary>
const int HEADER_SIZE = 40 + 8;

/// <summary>
/// MTU is expected to be atleast this number
/// </summary>
const int MIN_MTU = 1280;

/// <summary>
/// Max size of array that will be sent to or can be received from <see cref="ISocket"/>
/// <para>This will also be the size of all buffers used by <see cref="Peer"/></para>
/// <para>This is not max message size because this size includes packets header added by <see cref="Peer"/></para>
/// </summary>
// todo move these settings to socket
public static int MaxPacketSize => MIN_MTU - HEADER_SIZE;
}
}
1 change: 1 addition & 0 deletions Assets/Tests/Common/TestSocketFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public class TestSocketFactory : SocketFactory

int clientNameIndex;
int serverNameIndex;
public override int MaxPacketSize => 1300;
public override ISocket CreateClientSocket()
{
return new TestSocket($"Client {clientNameIndex++}");
Expand Down
3 changes: 2 additions & 1 deletion Assets/Tests/SocketLayer/AckSystem/AckSystemTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public static void SendReliable(this AckSystem ackSystem, byte[] array)
/// </summary>
public class AckSystemTestBase
{
public const int MAX_PACKET_SIZE = 1300;
protected readonly Random rand = new Random();
protected Pool<ByteBuffer> bufferPool = new Pool<ByteBuffer>(ByteBuffer.CreateNew, 1300, 100, 1000);
protected Pool<ByteBuffer> bufferPool = new Pool<ByteBuffer>(ByteBuffer.CreateNew, MAX_PACKET_SIZE, 100, 1000);

protected byte[] createRandomData(int id)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void ThrowsIfTooManyMessageAreSent()
{
connection = new SubIRawConnection()
};
instance.ackSystem = new AckSystem(instance.connection, config, time, bufferPool);
instance.ackSystem = new AckSystem(instance.connection, config, MAX_PACKET_SIZE, time, bufferPool);

for (int i = 0; i < 50; i++)
{
Expand Down Expand Up @@ -50,7 +50,7 @@ public void ThrowIfRingBufferIsfull()
{
connection = new SubIRawConnection()
};
instance.ackSystem = new AckSystem(instance.connection, config, time, bufferPool);
instance.ackSystem = new AckSystem(instance.connection, config, MAX_PACKET_SIZE, time, bufferPool);

for (int i = 0; i < 255; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ public class AckSystemTest_Fragmentation_Receive : AckSystemTestBase
public void SetUp()
{
config = new Config();
int mtu = config.MaxPacketSize;
int mtu = MAX_PACKET_SIZE;
int bigSize = (int)(mtu * 1.5f);

message = CreateBigData(1, bigSize);

var sender = new AckTestInstance();
sender.connection = new SubIRawConnection();
sender.ackSystem = new AckSystem(sender.connection, config, new Time(), bufferPool);
sender.ackSystem = new AckSystem(sender.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);
sender.ackSystem.SendReliable(message);
packet1 = sender.packet(0);
packet2 = sender.packet(1);


var connection = new SubIRawConnection();
ackSystem = new AckSystem(connection, config, new Time(), bufferPool);
ackSystem = new AckSystem(connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);
}

byte[] CreateBigData(int id, int size)
Expand Down Expand Up @@ -80,7 +80,7 @@ public void MessageShouldBeInQueueAfterReceive()

ackSystem.ReceiveReliable(packet2, packet2.Length, true);

int bytesIn1 = config.MaxPacketSize - AckSystem.MIN_RELIABLE_FRAGMENT_HEADER_SIZE;
int bytesIn1 = MAX_PACKET_SIZE - AckSystem.MIN_RELIABLE_FRAGMENT_HEADER_SIZE;
int bytesIn2 = message.Length - bytesIn1;

Assert.IsTrue(ackSystem.NextReliablePacket(out AckSystem.ReliableReceived first));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ public class AckSystemTest_Fragmentation_Send : AckSystemTestBase
public void SetUp()
{
var config = new Config();
int mtu = config.MaxPacketSize;
int mtu = MAX_PACKET_SIZE;
int bigSize = (int)(mtu * 1.5f);

byte[] message = CreateBigData(1, bigSize);

instance = new AckTestInstance();
instance.connection = new SubIRawConnection();
instance.ackSystem = new AckSystem(instance.connection, config, new Time(), bufferPool);
instance.ackSystem = new AckSystem(instance.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);

// create and send n messages
instance.messages = new List<byte[]>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void SetUp()
maxSequence = (ushort)((1 << config.SequenceSize) - 1);

connection = new SubIRawConnection();
ackSystem = new AckSystem(connection, config, new Time(), bufferPool);
ackSystem = new AckSystem(connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);

message = createRandomData(1);
ackSystem.SendNotify(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public void SetUp()

instance1 = new AckTestInstance();
instance1.connection = new SubIRawConnection();
instance1.ackSystem = new AckSystem(instance1.connection, config, new Time(), bufferPool);
instance1.ackSystem = new AckSystem(instance1.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);


instance2 = new AckTestInstance();
instance2.connection = new SubIRawConnection();
instance2.ackSystem = new AckSystem(instance2.connection, config, new Time(), bufferPool);
instance2.ackSystem = new AckSystem(instance2.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);

// create and send n messages
instance1.messages = new List<byte[]>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void SetUp()

instance = new AckTestInstance();
instance.connection = new SubIRawConnection();
instance.ackSystem = new AckSystem(instance.connection, new Config(), new Time(), bufferPool);
instance.ackSystem = new AckSystem(instance.connection, new Config(), MAX_PACKET_SIZE, new Time(), bufferPool);

// create and send n messages
instance.messages = new List<byte[]>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ public void SetUp()

instance1 = new AckTestInstance();
instance1.connection = new SubIRawConnection();
instance1.ackSystem = new AckSystem(instance1.connection, config, new Time(), bufferPool);
instance1.ackSystem = new AckSystem(instance1.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);
received1 = new List<ArraySegment<byte>>();


instance2 = new AckTestInstance();
instance2.connection = new SubIRawConnection();
instance2.ackSystem = new AckSystem(instance2.connection, config, new Time(), bufferPool);
instance2.ackSystem = new AckSystem(instance2.connection, config, MAX_PACKET_SIZE, new Time(), bufferPool);
received2 = new List<ArraySegment<byte>>();

// create and send n messages
Expand Down
4 changes: 2 additions & 2 deletions Assets/Tests/SocketLayer/AckSystem/AckSystemTest_Reliable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ public void SetUp()

instance1 = new AckTestInstance();
instance1.connection = new SubIRawConnection();
instance1.ackSystem = new AckSystem(instance1.connection, config, time, bufferPool);
instance1.ackSystem = new AckSystem(instance1.connection, config, MAX_PACKET_SIZE, time, bufferPool);


instance2 = new AckTestInstance();
instance2.connection = new SubIRawConnection();
instance2.ackSystem = new AckSystem(instance2.connection, config, time, bufferPool);
instance2.ackSystem = new AckSystem(instance2.connection, config, MAX_PACKET_SIZE, time, bufferPool);

badSocket = new BadSocket(instance1, instance2);

Expand Down
4 changes: 2 additions & 2 deletions Assets/Tests/SocketLayer/ByteUtilsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void WriteReadULongTest()
}

[Test]
[Explicit("c# only lets you bit shift by max of 63, It only takes first 6 bits, so will drop any extra bits and try to shift anyway")]
[Ignore("c# only lets you bit shift by max of 63, It only takes first 6 bits, so will drop any extra bits and try to shift anyway")]
public void UlongShift()
{
ulong value = 0xfUL;
Expand All @@ -100,7 +100,7 @@ public void UlongShift()

[Test]
[Description("this test should fail")]
[Explicit("c# only lets you bit shift by max of 63. It only takes first 6 bits, so will drop any extra bits and try to shift anyway")]
[Ignore("c# only lets you bit shift by max of 63. It only takes first 6 bits, so will drop any extra bits and try to shift anyway")]
public void UlongShift_ShouldFail()
{
ulong value = 0xfUL;
Expand Down

0 comments on commit 49c7f41

Please sign in to comment.