From 6dca9620eebfc37a2569e1c4cee917e1c53cea32 Mon Sep 17 00:00:00 2001 From: Forest Date: Sun, 16 Dec 2018 16:29:14 -0800 Subject: [PATCH] Reduce allocations when receiving packets --- Hazel.UnitTests/TestHelper.cs | 8 ++--- Hazel.UnitTests/UdpConnectionTests.cs | 45 ++++++++++++++++++++------- Hazel/Connection.cs | 6 ++-- Hazel/ConnectionListener.cs | 6 ++-- Hazel/DataReceivedEventArgs.cs | 6 ++-- Hazel/MessageReader.cs | 11 +++---- Hazel/NewConnectionEventArgs.cs | 8 ++--- Hazel/Udp/UdpConnection.Fragmented.cs | 10 +++++- Hazel/Udp/UdpConnection.cs | 13 +++++--- Hazel/Udp/UdpConnectionListener.cs | 19 +++++------ 10 files changed, 83 insertions(+), 49 deletions(-) diff --git a/Hazel.UnitTests/TestHelper.cs b/Hazel.UnitTests/TestHelper.cs index 1436ff6..08c44b7 100644 --- a/Hazel.UnitTests/TestHelper.cs +++ b/Hazel.UnitTests/TestHelper.cs @@ -35,11 +35,11 @@ namespace Hazel.UnitTests { Trace.WriteLine("Data was received correctly."); - Assert.AreEqual(data.Length, args.Bytes.Length); + Assert.AreEqual(data.Length, args.Message.Length); for (int i = 0; i < data.Length; i++) { - Assert.AreEqual(data[i], args.Bytes[i]); + Assert.AreEqual(data[i], args.Message.ReadByte()); } Assert.AreEqual(sendOption, args.SendOption); @@ -72,11 +72,11 @@ namespace Hazel.UnitTests { Trace.WriteLine("Data was received correctly."); - Assert.AreEqual(data.Length, innerArgs.Bytes.Length); + Assert.AreEqual(data.Length, innerArgs.Message.Length); for (int i = 0; i < data.Length; i++) { - Assert.AreEqual(data[i], innerArgs.Bytes[i]); + Assert.AreEqual(data[i], innerArgs.Message.ReadByte()); } Assert.AreEqual(sendOption, innerArgs.SendOption); diff --git a/Hazel.UnitTests/UdpConnectionTests.cs b/Hazel.UnitTests/UdpConnectionTests.cs index 3476653..575cdbf 100644 --- a/Hazel.UnitTests/UdpConnectionTests.cs +++ b/Hazel.UnitTests/UdpConnectionTests.cs @@ -39,31 +39,41 @@ namespace Hazel.UnitTests [TestMethod] public void UdpHandshakeTest() { + byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 }; using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296, IPMode.IPv4))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4))) { listener.Start(); + MessageReader output = null; listener.NewConnection += delegate (object sender, NewConnectionEventArgs e) { - Assert.IsTrue(Enumerable.SequenceEqual(e.HandshakeData, new byte[] { 1, 2, 3, 4, 5, 6 })); + output = e.HandshakeData; }; - connection.Connect(new byte[] { 1, 2, 3, 4, 5, 6 }); + connection.Connect(TestData); + + Thread.Sleep(10); + for (int i = 0; i < TestData.Length; ++i) + { + Assert.AreEqual(TestData[i], output.ReadByte()); + } } } [TestMethod] public void UdpUnreliableMessageSendTest() { + byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 }; using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296, IPMode.IPv4))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4))) { + MessageReader output = null; listener.NewConnection += delegate (object sender, NewConnectionEventArgs e) { e.Connection.DataReceived += delegate (object s, DataReceivedEventArgs evt) { - Assert.IsTrue(Enumerable.SequenceEqual(evt.Bytes, new byte[] { 1, 2, 3, 4, 5, 6 })); + output = evt.Message; }; }; @@ -73,10 +83,16 @@ namespace Hazel.UnitTests for (int i = 0; i < 4; ++i) { var msg = MessageWriter.Get(SendOption.None); - msg.Write(new byte[] { 1, 2, 3, 4, 5, 6 }); + msg.Write(TestData); connection.Send(msg); msg.Recycle(); } + + Thread.Sleep(10); + for (int i = 0; i < TestData.Length; ++i) + { + Assert.AreEqual(TestData[i], output.ReadByte()); + } } } @@ -91,7 +107,7 @@ namespace Hazel.UnitTests { e.Connection.DataReceived += delegate (object s, DataReceivedEventArgs evt) { - Assert.IsTrue(Enumerable.SequenceEqual(evt.Bytes, new byte[] { 3, 4 })); + Assert.IsTrue(Enumerable.SequenceEqual(evt.Message.Buffer, new byte[] { 3, 4 })); }; }; @@ -280,13 +296,18 @@ namespace Hazel.UnitTests Thread.Sleep(1050); //Enough time for ~10 keep alive packets - Assert.IsTrue( - args.Connection.Statistics.TotalBytesSent >= 30 && - args.Connection.Statistics.TotalBytesSent <= 50, - "Sent: " + args.Connection.Statistics.TotalBytesSent - ); - - mutex.Set(); + try + { + Assert.IsTrue( + args.Connection.Statistics.TotalBytesSent >= 30 && + args.Connection.Statistics.TotalBytesSent <= 50, + "Sent: " + args.Connection.Statistics.TotalBytesSent + ); + } + finally + { + mutex.Set(); + } }; listener.Start(); diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs index 5bb765f..5a02b40 100644 --- a/Hazel/Connection.cs +++ b/Hazel/Connection.cs @@ -236,17 +236,17 @@ namespace Hazel /// /// Invokes the DataReceived event. /// - /// The bytes received. + /// The bytes received. /// The the message was received with. /// /// Invokes the event on this connection to alert subscribers a new message has been /// received. The bytes and the send option that the message was sent with should be passed in to give to the /// subscribers. /// - protected void InvokeDataReceived(byte[] bytes, SendOption sendOption, ushort reliableId) + protected void InvokeDataReceived(MessageReader msg, SendOption sendOption, ushort reliableId) { DataReceivedEventArgs args = DataReceivedEventArgs.GetObject(); - args.Set(bytes, sendOption, reliableId); + args.Set(msg, sendOption, reliableId); //Make a copy to avoid race condition between null check and invocation EventHandler handler = DataReceived; diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs index 5aaf6d7..eadf2b1 100644 --- a/Hazel/ConnectionListener.cs +++ b/Hazel/ConnectionListener.cs @@ -68,17 +68,17 @@ namespace Hazel /// /// Invokes the NewConnection event with the supplied connection. /// - /// The user sent bytes that were received as part of the handshake. + /// The user sent bytes that were received as part of the handshake. /// The connection to pass in the arguments. /// /// Implementers should call this to invoke the event before data is received so that /// subscribers do not miss any data that may have been sent immediately after connecting. /// - protected void InvokeNewConnection(byte[] bytes, Connection connection) + protected void InvokeNewConnection(MessageReader msg, Connection connection) { //Get new args NewConnectionEventArgs args = NewConnectionEventArgs.GetObject(); - args.Set(bytes, connection); + args.Set(msg, connection); //Make a copy to avoid race condition between null check and invocation EventHandler handler = NewConnection; diff --git a/Hazel/DataReceivedEventArgs.cs b/Hazel/DataReceivedEventArgs.cs index ffdcce0..69446e4 100644 --- a/Hazel/DataReceivedEventArgs.cs +++ b/Hazel/DataReceivedEventArgs.cs @@ -35,7 +35,7 @@ namespace Hazel /// /// The bytes received from the client. /// - public byte[] Bytes { get; private set; } + public MessageReader Message { get; private set; } /// /// The the data was sent with. @@ -57,9 +57,9 @@ namespace Hazel /// /// The bytes received. /// The send option used to send the data. - internal void Set(byte[] bytes, SendOption sendOption, ushort reliableId) + internal void Set(MessageReader msg, SendOption sendOption, ushort reliableId) { - this.Bytes = bytes; + this.Message = msg; this.SendOption = sendOption; this.ReliableId = reliableId; } diff --git a/Hazel/MessageReader.cs b/Hazel/MessageReader.cs index 49e2e64..f6951b3 100644 --- a/Hazel/MessageReader.cs +++ b/Hazel/MessageReader.cs @@ -29,14 +29,13 @@ namespace Hazel private int readHead; - public static MessageReader Get(MessageReader srcMsg) + public static MessageReader GetRaw(byte[] bytes, int offset, int length) { var output = ReaderPool.GetObject(); - output.Buffer = srcMsg.Buffer; - output.Offset = srcMsg.Offset; - output.Position = srcMsg.Position; - output.Length = srcMsg.Length; - output.Tag = srcMsg.Tag; + output.Buffer = bytes; + output.Offset = offset; + output.Position = 0; + output.Length = length; return output; } diff --git a/Hazel/NewConnectionEventArgs.cs b/Hazel/NewConnectionEventArgs.cs index f6e911d..b757284 100644 --- a/Hazel/NewConnectionEventArgs.cs +++ b/Hazel/NewConnectionEventArgs.cs @@ -35,7 +35,7 @@ namespace Hazel /// /// The data received from the client in the handshake. /// - public byte[] HandshakeData { get; private set; } + public MessageReader HandshakeData { get; private set; } /// /// The to the new client. @@ -53,11 +53,11 @@ namespace Hazel /// /// Sets the members of the arguments. /// - /// The bytes that were received in the handshake. + /// The bytes that were received in the handshake. /// The new connection - internal void Set(byte[] bytes, Connection connection) + internal void Set(MessageReader msg, Connection connection) { - this.HandshakeData = bytes; + this.HandshakeData = msg; this.Connection = connection; } diff --git a/Hazel/Udp/UdpConnection.Fragmented.cs b/Hazel/Udp/UdpConnection.Fragmented.cs index bf3b49d..9e525b2 100644 --- a/Hazel/Udp/UdpConnection.Fragmented.cs +++ b/Hazel/Udp/UdpConnection.Fragmented.cs @@ -165,7 +165,15 @@ namespace Hazel.Udp ptr += fragment.data.Length - fragment.offset; } - InvokeDataReceived(completeData, SendOption.FragmentedReliable, 0); + var reader = MessageReader.GetRaw(completeData, 0, completeData.Length); + try + { + InvokeDataReceived(reader, SendOption.FragmentedReliable, 0); + } + finally + { + reader.Recycle(); + } } /// diff --git a/Hazel/Udp/UdpConnection.cs b/Hazel/Udp/UdpConnection.cs index 16a85d9..926a616 100644 --- a/Hazel/Udp/UdpConnection.cs +++ b/Hazel/Udp/UdpConnection.cs @@ -242,10 +242,15 @@ namespace Hazel.Udp /// The offset of data in the buffer. void InvokeDataReceived(SendOption sendOption, byte[] buffer, int dataOffset, ushort reliableId) { - byte[] dataBytes = new byte[buffer.Length - dataOffset]; - Buffer.BlockCopy(buffer, dataOffset, dataBytes, 0, dataBytes.Length); - - InvokeDataReceived(dataBytes, sendOption, reliableId); + var reader = MessageReader.GetRaw(buffer, dataOffset, buffer.Length - dataOffset); + try + { + InvokeDataReceived(reader, sendOption, reliableId); + } + finally + { + reader.Recycle(); + } } /// diff --git a/Hazel/Udp/UdpConnectionListener.cs b/Hazel/Udp/UdpConnectionListener.cs index 44410d2..c8f5333 100644 --- a/Hazel/Udp/UdpConnectionListener.cs +++ b/Hazel/Udp/UdpConnectionListener.cs @@ -153,14 +153,9 @@ namespace Hazel.Udp UdpServerConnection connection; lock (connections) { - aware = connections.ContainsKey(remoteEndPoint); - //If we're aware of this connection use the one already - if (aware) - connection = connections[remoteEndPoint]; - //If this is a new client then connect with them! - else + if (!(aware = connections.TryGetValue(remoteEndPoint, out connection))) { //Check for malformed connection attempts if (buffer[0] != (byte)UdpSendOption.Hello) @@ -177,9 +172,15 @@ namespace Hazel.Udp //If it's a new connection invoke the NewConnection event. if (!aware) { - byte[] dataBuffer = new byte[buffer.Length - 3]; - Buffer.BlockCopy(buffer, 3, dataBuffer, 0, buffer.Length - 3); - InvokeNewConnection(dataBuffer, connection); + var reader = MessageReader.GetRaw(buffer, 4, buffer.Length - 4); + try + { + InvokeNewConnection(reader, connection); + } + finally + { + reader.Recycle(); + } } } -- 2.39.5