diff --git a/HKMP/Networking/Client/UdpNetClient.cs b/HKMP/Networking/Client/UdpNetClient.cs index 5f94d60..397df0c 100644 --- a/HKMP/Networking/Client/UdpNetClient.cs +++ b/HKMP/Networking/Client/UdpNetClient.cs @@ -76,10 +76,11 @@ private void ReceiveData(CancellationToken token) { EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); while (!token.IsCancellationRequested) { + var numReceived = 0; var buffer = new byte[MaxUdpPacketSize]; try { - UdpSocket.ReceiveFrom( + numReceived = UdpSocket.ReceiveFrom( buffer, SocketFlags.None, ref endPoint @@ -88,7 +89,7 @@ ref endPoint Logger.Error($"UDP Socket exception:\n{e}"); } - var packets = PacketManager.HandleReceivedData(buffer, ref _leftoverData); + var packets = PacketManager.HandleReceivedData(buffer, numReceived, ref _leftoverData); _onReceive?.Invoke(packets); } diff --git a/HKMP/Networking/Packet/PacketManager.cs b/HKMP/Networking/Packet/PacketManager.cs index d2f4236..2b7b74f 100644 --- a/HKMP/Networking/Packet/PacketManager.cs +++ b/HKMP/Networking/Packet/PacketManager.cs @@ -479,10 +479,19 @@ Action handler /// /// Handle received data and leftover data and store subsequent leftover data again. /// - /// Byte array of received data. + /// Byte array of the buffer containing received data. The entirety of the array does not + /// necessarily contain received bytes. Rather, the first bytes are received data. + /// + /// Number of received bytes in the buffer. /// Reference byte array that should be filled with leftover data. /// A list of packets that were constructed from the received data. - public static List HandleReceivedData(byte[] receivedData, ref byte[] leftoverData) { + public static List HandleReceivedData(byte[] buffer, int numReceived, ref byte[] leftoverData) { + // Make a new byte array exactly the length of number of received bytes and fill it + var receivedData = new byte[numReceived]; + for (var i = 0; i < numReceived; i++) { + receivedData[i] = buffer[i]; + } + var currentData = receivedData; // Check whether we have leftover data from the previous read, and concatenate the two byte arrays @@ -520,8 +529,7 @@ private static List ByteArrayToPackets(byte[] data, ref byte[] leftover) // The only break from this loop is when there is no new packet to be read do { - // If there is still an int (4 bytes) to read in the data, - // it represents the next packet's length + // If there is still 2 bytes to read in the data, it represents the next packet's length var packetLength = 0; var unreadDataLength = data.Length - readIndex; if (unreadDataLength > 1) { @@ -534,15 +542,14 @@ private static List ByteArrayToPackets(byte[] data, ref byte[] leftover) break; } - // Check whether our given data array actually contains - // the same number of bytes as the packet length + // Check whether our given data array actually contains the same number of bytes as the packet length if (data.Length - readIndex < packetLength) { // There is not enough bytes in the data array to fill the requested packet with // So we put everything, including the packet length ushort (2 bytes) into the leftover byte array leftover = new byte[unreadDataLength]; for (var i = 0; i < unreadDataLength; i++) { - // Make sure to index data 2 bytes earlier, since we incremented - // when we read the packet length ushort + // Make sure to index data 2 bytes earlier, since we incremented when we read the packet + // length ushort leftover[i] = data[readIndex - 2 + i]; } diff --git a/HKMP/Networking/Server/NetServer.cs b/HKMP/Networking/Server/NetServer.cs index 195cfb6..4d12a98 100644 --- a/HKMP/Networking/Server/NetServer.cs +++ b/HKMP/Networking/Server/NetServer.cs @@ -41,11 +41,6 @@ internal class NetServer : INetServer { /// private readonly PacketManager _packetManager; - /// - /// Object to lock asynchronous access when dealing with clients. - /// - private readonly object _clientLock = new object(); - /// /// Dictionary mapping client IDs to net server clients. /// @@ -155,11 +150,12 @@ private void ReceiveData(CancellationToken token) { EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); while (!token.IsCancellationRequested) { + var numReceived = 0; var buffer = new byte[MaxUdpPacketSize]; try { // This will block until data is available - _udpSocket.ReceiveFrom( + numReceived = _udpSocket.ReceiveFrom( buffer, SocketFlags.None, ref endPoint @@ -169,7 +165,8 @@ ref endPoint } _receivedQueue.Enqueue(new ReceivedData { - Data = buffer, + Buffer = buffer, + NumReceived = numReceived, EndPoint = endPoint as IPEndPoint }); _processingWaitHandle.Set(); @@ -192,7 +189,8 @@ private void StartProcessing(CancellationToken token) { while (_receivedQueue.TryDequeue(out var receivedData)) { var packets = PacketManager.HandleReceivedData( - receivedData.Data, + receivedData.Buffer, + receivedData.NumReceived, ref _leftoverData ); @@ -529,12 +527,17 @@ Func packetInstantiator /// internal class ReceivedData { /// - /// Byte array of received data. + /// Byte array of the buffer containing received data. + /// + public byte[] Buffer { get; init; } + + /// + /// The number of bytes in the buffer that were received. The rest of the buffer is empty. /// - public byte[] Data { get; set; } + public int NumReceived { get; init; } /// /// The IP end-point of the client from which we received the data. /// - public IPEndPoint EndPoint { get; set; } + public IPEndPoint EndPoint { get; init; } } diff --git a/HKMP/Networking/UdpUpdateManager.cs b/HKMP/Networking/UdpUpdateManager.cs index 835fc9c..810954c 100644 --- a/HKMP/Networking/UdpUpdateManager.cs +++ b/HKMP/Networking/UdpUpdateManager.cs @@ -27,6 +27,13 @@ internal abstract class UdpUpdateManager : UdpUpdateManage /// private const int ConnectionTimeout = 5000; + /// + /// The MTU (maximum transfer unit) to use to send packets with. If the length of a packet exceeds this, we break + /// it up into smaller packets before sending. This ensures that we control the breaking of packets in most + /// cases and do not rely on smaller network devices for the breaking up as this could impact performance. + /// + private const int PacketMtu = 1400; + /// /// The number of sequence numbers to store in the received queue to construct ack fields with and /// to check against resent data. @@ -234,6 +241,30 @@ private void CreateAndSendUpdatePacket() { // Increase (and potentially wrap) the current local sequence number _localSequence++; + // Check if the packet exceeds (usual) MTU and break it up if so + if (packet.Length > PacketMtu) { + // Get the original packet's bytes as an array + var byteArray= packet.ToArray(); + + // Keep track of the index in the original array for copying + var index = 0; + // While we have not reached the end of the original array yet with the index + while (index < byteArray.Length) { + // Take the minimum of what's left to copy in the original array and the max MTU + var length = System.Math.Min(byteArray.Length - index, PacketMtu); + // Create a new array that is this calculated length + var newBytes = new byte[length]; + // Copy over the length of bytes starting from index into the new array + Array.Copy(byteArray, index, newBytes, 0, length); + + SendPacket(new Packet.Packet(newBytes)); + + index += length; + } + + return; + } + SendPacket(packet); }