From 8543ec1fd037ee03496419600f5733c53aa2a11d Mon Sep 17 00:00:00 2001 From: Daniel Baetz Date: Wed, 31 Jul 2019 18:12:01 +0200 Subject: [PATCH 1/4] #26 - fixed bug - unable to run after issue --- Exomia.Network/ServerBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Exomia.Network/ServerBase.cs b/Exomia.Network/ServerBase.cs index 5cad1a9..f76134c 100644 --- a/Exomia.Network/ServerBase.cs +++ b/Exomia.Network/ServerBase.cs @@ -161,13 +161,13 @@ private protected ServerBase() public bool Run(int port) { if (_isRunning) { return true; } - _isRunning = true; - _port = port; + _port = port; if (OnRun(port, out _listener)) { _state = RECEIVE_FLAG | SEND_FLAG; ListenAsync(); + _isRunning = true; return true; } return false; From b9fddf5be3aa5e720b2a9d2570e17be5453df096 Mon Sep 17 00:00:00 2001 From: Daniel Baetz Date: Thu, 1 Aug 2019 23:58:30 +0200 Subject: [PATCH 2/4] #27 - added support for any payload length --- Example.Client/App.config | 7 + Example.Client/Example.Client.csproj | 61 ++++ Example.Client/Program.cs | 75 +++++ Example.Client/Properties/AssemblyInfo.cs | 45 +++ Example.Server/App.config | 7 + Example.Server/Example.Server.csproj | 61 ++++ Example.Server/Program.cs | 79 +++++ Example.Server/Properties/AssemblyInfo.cs | 45 +++ .../CircularBuffer.UnitTest1.cs | 50 ++-- .../Exomia.Network.UnitTest.csproj | 1 - .../Serialization.Tcp.UnitTest1.cs | 140 --------- Exomia.Network.sln | 36 ++- Exomia.Network/AssemblyInfo.cs | 13 + Exomia.Network/ClientBase.cs | 188 ++++++++---- Exomia.Network/Constants.cs | 69 ++++- Exomia.Network/Encoding/PayloadEncoding.cs | 59 ++-- Exomia.Network/Exomia.Network.csproj | 2 +- Exomia.Network/Lib/ServerClientEventEntry.cs | 2 +- Exomia.Network/Native/CircularBuffer.cs | 245 ++++------------ .../Serialization/Serialization.Tcp.cs | 262 +++++++++++------ .../Serialization/Serialization.Udp.cs | 276 ++++++++++-------- Exomia.Network/Serialization/Serialization.cs | 53 ---- Exomia.Network/ServerBase.cs | 247 ++++++++++++++-- Exomia.Network/TCP/TcpClientApm.cs | 89 +++--- Exomia.Network/TCP/TcpClientBase.cs | 113 +------ Exomia.Network/TCP/TcpClientEap.cs | 94 +++--- Exomia.Network/TCP/TcpServerApmBase.cs | 87 +++--- Exomia.Network/TCP/TcpServerBase.cs | 112 +------ Exomia.Network/TCP/TcpServerEapBase.cs | 101 ++++--- Exomia.Network/UDP/UdpClientApm.cs | 88 +++--- Exomia.Network/UDP/UdpClientBase.cs | 72 +---- Exomia.Network/UDP/UdpClientEap.cs | 101 ++++--- Exomia.Network/UDP/UdpServerApmBase.cs | 81 ++--- Exomia.Network/UDP/UdpServerBase.cs | 83 +----- Exomia.Network/UDP/UdpServerEapBase.cs | 123 ++++---- README.md | 4 +- 36 files changed, 1700 insertions(+), 1471 deletions(-) create mode 100644 Example.Client/App.config create mode 100644 Example.Client/Example.Client.csproj create mode 100644 Example.Client/Program.cs create mode 100644 Example.Client/Properties/AssemblyInfo.cs create mode 100644 Example.Server/App.config create mode 100644 Example.Server/Example.Server.csproj create mode 100644 Example.Server/Program.cs create mode 100644 Example.Server/Properties/AssemblyInfo.cs delete mode 100644 Exomia.Network.UnitTest/Serialization.Tcp.UnitTest1.cs create mode 100644 Exomia.Network/AssemblyInfo.cs delete mode 100644 Exomia.Network/Serialization/Serialization.cs diff --git a/Example.Client/App.config b/Example.Client/App.config new file mode 100644 index 0000000..56f623a --- /dev/null +++ b/Example.Client/App.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Example.Client/Example.Client.csproj b/Example.Client/Example.Client.csproj new file mode 100644 index 0000000..9b92a05 --- /dev/null +++ b/Example.Client/Example.Client.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {90A9560E-16E9-4008-B061-B5827AF69880} + Exe + Example.Client + Example.Client + v4.7.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + + + + + + + + + + + + + + + + + + + {4a17dccb-d3de-48df-97a7-f46936096ca6} + Exomia.Network + + + + \ No newline at end of file diff --git a/Example.Client/Program.cs b/Example.Client/Program.cs new file mode 100644 index 0000000..c4bafdd --- /dev/null +++ b/Example.Client/Program.cs @@ -0,0 +1,75 @@ +#region License + +// Copyright (c) 2018-2019, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +#define TCP + +using System; +using System.Text; +using System.Threading.Tasks; +using Exomia.Network; +using Exomia.Network.DefaultPackets; +using Exomia.Network.TCP; + +namespace Example.Client +{ + class Program + { + private static async Task Main(string[] args) + { +#if TCP + using (TcpClientEap client = new TcpClientEap()) +#else + using (var client = new UdpClientEap()) +#endif + { + client.Disconnected += (c, r) => { Console.WriteLine(r + " | Disconnected"); }; + client.AddCommand( + (in Packet packet) => + { + return Encoding.UTF8.GetString(packet.Buffer, packet.Offset, packet.Length); + }, 45); + + client.AddDataReceivedCallback( + 45, (client1, data) => + { + Console.WriteLine(data + " -- OK"); + return true; + }); + + Console.WriteLine(client.Connect("127.0.0.1", 3000) ? "CONNECTED" : "CONNECT FAILED"); + + byte[] request = Encoding.UTF8.GetBytes("get time"); + for (int i = 0; i < 10; i++) + { + Response res = await client.SendRPing(); + if (res) + { + Console.WriteLine( + i + + "ping received " + TimeSpan.FromTicks((DateTime.Now.Ticks - res.Result.Timestamp) / 2) + .TotalMilliseconds); + } + else { Console.WriteLine("error receiving response"); } + + Response res2 = await client.SendR( + 45, request, 0, request.Length, (in Packet packet) => + { + return Encoding.UTF8.GetString(packet.Buffer, packet.Offset, packet.Length); + }); + + Console.WriteLine(res2 ? res2.Result : "error receiving response"); + } + + Console.WriteLine("press any key to exit..."); + Console.ReadKey(); + } + } + } +} \ No newline at end of file diff --git a/Example.Client/Properties/AssemblyInfo.cs b/Example.Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8bc52a4 --- /dev/null +++ b/Example.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +#region License + +// Copyright (c) 2018-2019, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Example.Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Example.Client")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("90a9560e-16e9-4008-b061-b5827af69880")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Example.Server/App.config b/Example.Server/App.config new file mode 100644 index 0000000..56f623a --- /dev/null +++ b/Example.Server/App.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Example.Server/Example.Server.csproj b/Example.Server/Example.Server.csproj new file mode 100644 index 0000000..1c29ef5 --- /dev/null +++ b/Example.Server/Example.Server.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF} + Exe + Example.Server + Example.Server + v4.7.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + + + + + + + + + + + + + + + + + + + {4a17dccb-d3de-48df-97a7-f46936096ca6} + Exomia.Network + + + + \ No newline at end of file diff --git a/Example.Server/Program.cs b/Example.Server/Program.cs new file mode 100644 index 0000000..46bcf10 --- /dev/null +++ b/Example.Server/Program.cs @@ -0,0 +1,79 @@ +#region License + +// Copyright (c) 2018-2019, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +#define TCP + +using System; +using System.Text; +using Exomia.Network; +using Exomia.Network.TCP; + +namespace Example.Server +{ + class Program + { + private static void Main(string[] args) + { + using (Server server = new Server()) + { + server.ClientConnected += (server1, client) => + { + Console.WriteLine("Client connected: " + client.IPAddress); + }; + server.ClientDisconnected += (server1, client, reason) => + { + Console.WriteLine(reason + " Client disconnected: " + client.IPAddress); + }; + server.AddCommand( + (in Packet packet) => + { + return Encoding.UTF8.GetString(packet.Buffer, packet.Offset, packet.Length); + }, 45); + server.AddDataReceivedCallback( + 45, (server1, client, data, responseid) => + { + string request = (string)data; + Console.WriteLine($"Request: {request}"); + byte[] buffer = Encoding.UTF8.GetBytes(DateTime.Now.ToLongDateString()); + server1.SendTo(client, 45, buffer, 0, buffer.Length, responseid); + return true; + }); + + Console.WriteLine(server.Run(3000)); + + Console.WriteLine("press any key to exit..."); + Console.ReadKey(); + } + } + } + +#if TCP + class Server : TcpServerEapBase +#else + class Server : UdpServerEapBase +#endif + { + protected override bool CreateServerClient(out ServerClient serverClient) + { + serverClient = new ServerClient(); + return true; + } + + public Server(ushort expectedMaxClient = 32, ushort maxPacketSize = 65520) + : base(expectedMaxClient, maxPacketSize) { } + } + +#if TCP + class ServerClient : TcpServerClientBase +#else + class ServerClient : UdpServerClientBase +#endif + { } +} \ No newline at end of file diff --git a/Example.Server/Properties/AssemblyInfo.cs b/Example.Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b093163 --- /dev/null +++ b/Example.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +#region License + +// Copyright (c) 2018-2019, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Example.Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Example.Server")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7e2a2465-b98e-42b8-b8ab-de890f2406bf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Exomia.Network.UnitTest/CircularBuffer.UnitTest1.cs b/Exomia.Network.UnitTest/CircularBuffer.UnitTest1.cs index bd5d954..f308bb5 100644 --- a/Exomia.Network.UnitTest/CircularBuffer.UnitTest1.cs +++ b/Exomia.Network.UnitTest/CircularBuffer.UnitTest1.cs @@ -90,15 +90,15 @@ public void UnsafeWriteTest() byte[] buffer = { 45, 48, 72, 15 }; fixed (byte* src = buffer) { - cb.Write(src, 0, 4); + cb.Write(src, 4); Assert.AreEqual(cb.Count, 4); - cb.Write(src, 2, 2); + cb.Write(src + 2, 2); Assert.AreEqual(cb.Count, 6); - cb.Write(src, 1, 2); + cb.Write(src + 1, 2); Assert.AreEqual(cb.Count, 8); } @@ -149,7 +149,7 @@ public void UnsafeReadTest() byte[] readBuffer = new byte[4]; fixed (byte* src = readBuffer) { - cb.Read(src, 0, readBuffer.Length, 0); + cb.Read(src, readBuffer.Length, 0); } Assert.AreEqual(cb.Count, 0); @@ -158,7 +158,7 @@ public void UnsafeReadTest() Assert.IsTrue(readBuffer.SequenceEqual(buffer)); fixed (byte* src = readBuffer) { - Assert.AreEqual(0, cb.Read(src, 0, readBuffer.Length, 0)); + Assert.AreEqual(0, cb.Read(src, readBuffer.Length, 0)); } byte[] buffer2 = { 45, 48, 72, 1, 4, 87, 95 }; @@ -167,7 +167,7 @@ public void UnsafeReadTest() byte[] readBuffer2 = new byte[buffer2.Length]; fixed (byte* src = readBuffer2) { - cb.Read(src, 0, buffer2.Length - 2, 2); + cb.Read(src, buffer2.Length - 2, 2); } Assert.IsTrue(readBuffer2.Take(buffer2.Length - 2).SequenceEqual(buffer2.Skip(2))); @@ -210,15 +210,15 @@ public void UnsafeWriteTest_With_Overflow() fixed (byte* src = buffer) { - cb.Write(src, 0, buffer.Length); + cb.Write(src, buffer.Length); Assert.AreEqual(cb.Count, 77); - cb.Write(src, 0, buffer.Length); + cb.Write(src, buffer.Length); Assert.AreEqual(cb.Count, 128); - cb.Write(src, 0, buffer.Length); + cb.Write(src, buffer.Length); Assert.AreEqual(cb.Count, 128); } @@ -305,13 +305,13 @@ public void UnsafeReadTest_With_Overflow() byte[] dummy = new byte[100]; fixed (byte* src = dummy) { - Assert.AreEqual(0, cb.Read(src, 0, 78, 0)); + Assert.AreEqual(0, cb.Read(src, 78, 0)); } cb.Write(buffer, 0, buffer.Length); fixed (byte* src = dummy) { - Assert.AreEqual(buffer.Length, cb.Read(src, 0, 78, 0)); + Assert.AreEqual(buffer.Length, cb.Read(src, 78, 0)); } cb.Dispose(); @@ -326,7 +326,7 @@ public void UnsafeReadTest_With_Overflow() byte[] readBuffer2 = new byte[9]; fixed (byte* src = readBuffer2) { - Assert.AreEqual(readBuffer2.Length, cb.Read(src, 0, readBuffer2.Length, 0)); + Assert.AreEqual(readBuffer2.Length, cb.Read(src, readBuffer2.Length, 0)); } Assert.AreEqual(cb.Count, 16 - 9); @@ -342,7 +342,7 @@ public void UnsafeReadTest_With_Overflow() byte[] readBuffer4 = new byte[9]; fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Read(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Read(src, readBuffer4.Length, 0)); } Assert.AreEqual(cb.Count, 0); @@ -351,7 +351,7 @@ public void UnsafeReadTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Read(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Read(src, readBuffer4.Length, 0)); } Assert.AreEqual(cb.Count, 0); Assert.IsTrue(cb.IsEmpty); @@ -362,7 +362,7 @@ public void UnsafeReadTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Read(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Read(src, readBuffer4.Length, 0)); } Assert.AreEqual(cb.Count, 0); Assert.IsTrue(cb.IsEmpty); @@ -370,7 +370,7 @@ public void UnsafeReadTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(1, cb.Read(src, 0, 1, 8)); + Assert.AreEqual(1, cb.Read(src, 1, 8)); } Assert.AreEqual(cb.Count, 0); Assert.IsTrue(cb.IsEmpty); @@ -424,7 +424,7 @@ public void UnsafePeekTest() fixed (byte* dest = peekBuffer) { - cb.Peek(dest, 0, peekBuffer.Length, 0); + cb.Peek(dest, peekBuffer.Length, 0); } Assert.AreEqual(cb.Count, 4); Assert.IsFalse(cb.IsEmpty); @@ -440,7 +440,7 @@ public void UnsafePeekTest() fixed (byte* dest = peekBuffer2) { - cb.Peek(peekBuffer2, 0, buffer2.Length - 2, 4 + 2); + cb.Peek(dest, buffer2.Length - 2, 4 + 2); } Assert.IsTrue(peekBuffer2.Take(buffer2.Length - 2).SequenceEqual(buffer2.Skip(2))); @@ -530,13 +530,13 @@ public void UnsafePeekTest_With_Overflow() fixed (byte* src = dummy) { - Assert.AreEqual(0, cb.Peek(src, 0, 78, 0)); + Assert.AreEqual(0, cb.Peek(src, 78, 0)); } cb.Write(buffer, 0, buffer.Length); fixed (byte* src = dummy) { - Assert.AreEqual(buffer.Length, cb.Peek(src, 0, 78, 0)); + Assert.AreEqual(buffer.Length, cb.Peek(src, 78, 0)); } cb.Dispose(); @@ -550,7 +550,7 @@ public void UnsafePeekTest_With_Overflow() byte[] readBuffer2 = new byte[9]; fixed (byte* src = readBuffer2) { - Assert.AreEqual(7, cb.Peek(src, 0, readBuffer2.Length, 9)); + Assert.AreEqual(7, cb.Peek(src, readBuffer2.Length, 9)); } Assert.AreEqual(cb.Count, 16); Assert.IsFalse(cb.IsEmpty); @@ -565,7 +565,7 @@ public void UnsafePeekTest_With_Overflow() byte[] readBuffer4 = new byte[9]; fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Read(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Read(src, readBuffer4.Length, 0)); } Assert.AreEqual(0, cb.Count); Assert.IsTrue(cb.IsEmpty); @@ -573,7 +573,7 @@ public void UnsafePeekTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Peek(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Peek(src, readBuffer4.Length, 0)); } Assert.AreEqual(cb.Count, 9); Assert.IsFalse(cb.IsEmpty); @@ -584,7 +584,7 @@ public void UnsafePeekTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(9, cb.Read(src, 0, readBuffer4.Length, 0)); + Assert.AreEqual(9, cb.Read(src, readBuffer4.Length, 0)); } Assert.AreEqual(0, cb.Count); Assert.IsTrue(cb.IsEmpty); @@ -592,7 +592,7 @@ public void UnsafePeekTest_With_Overflow() cb.Write(buffer, 0, buffer.Length); fixed (byte* src = readBuffer4) { - Assert.AreEqual(1, cb.Peek(src, 0, 1, 8)); + Assert.AreEqual(1, cb.Peek(src, 1, 8)); } Assert.AreEqual(9, cb.Count); Assert.IsFalse(cb.IsEmpty); diff --git a/Exomia.Network.UnitTest/Exomia.Network.UnitTest.csproj b/Exomia.Network.UnitTest/Exomia.Network.UnitTest.csproj index e237a15..0bfb9b2 100644 --- a/Exomia.Network.UnitTest/Exomia.Network.UnitTest.csproj +++ b/Exomia.Network.UnitTest/Exomia.Network.UnitTest.csproj @@ -46,7 +46,6 @@ - diff --git a/Exomia.Network.UnitTest/Serialization.Tcp.UnitTest1.cs b/Exomia.Network.UnitTest/Serialization.Tcp.UnitTest1.cs deleted file mode 100644 index 6fba212..0000000 --- a/Exomia.Network.UnitTest/Serialization.Tcp.UnitTest1.cs +++ /dev/null @@ -1,140 +0,0 @@ -#region License - -// Copyright (c) 2018-2019, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -using System; -using Exomia.Network.Lib; -using Exomia.Network.Native; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Exomia.Network.UnitTest -{ - [TestClass] - public class SerializationTcpUnitTest1 - { - [TestMethod] - public unsafe void SerializeTcpNoResponseTest() - { - byte[] data = { 0b1000_0000, 0b1000_0000, 0b0000_0000, 0b1000_0000, 0b1000_0000, 0b0000_0000, 0b1000_0000 }; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeTcp( - 1337u, src, data.Length, 0, EncryptionMode.None, CompressionMode.Lz4, out byte[] send, - out int size); - Assert.AreEqual(size, Constants.TCP_HEADER_SIZE + data.Length + 1 + 1); - - CircularBuffer cb = new CircularBuffer(); - cb.Write(send, 0, size); - - Assert.IsTrue( - cb.PeekHeader( - 0, out byte packetHeader, out uint commandID, out int dataLength, out ushort checksum)); - - Assert.AreEqual((byte)0, packetHeader); - Assert.AreEqual(1337u, commandID); - Assert.AreEqual(data.Length + 1 + 1, dataLength); - - Assert.IsTrue(cb.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b)); - Assert.AreEqual((byte)0, b); - - cb.Read(send, 0, dataLength, Constants.TCP_HEADER_SIZE); - - byte[] deserializeBuffer = new byte[128]; - /*int checksum2 = Serialization.Serialization.Deserialize( - send, 0, dataLength - 1, deserializeBuffer, out int bufferLength); - - Assert.AreEqual(checksum, checksum2); - - Assert.AreEqual(data.Length, bufferLength); - Assert.IsTrue(data.SequenceEqual(deserializeBuffer.Take(bufferLength)));*/ - } - } - - [TestMethod] - public unsafe void SerializeTcpResponseTest() - { - byte[] data = { 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000 }; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeTcp( - 1337u, src, data.Length, 654584478, EncryptionMode.None, CompressionMode.Lz4, - out byte[] send, out int size); - Assert.AreEqual(size, Constants.TCP_HEADER_SIZE + 4 + data.Length + 1 + 1); - - CircularBuffer cb = new CircularBuffer(); - cb.Write(send, 0, size); - - Assert.IsTrue( - cb.PeekHeader( - 0, out byte packetHeader, out uint commandID, out int dataLength, out ushort checksum)); - - Assert.AreEqual((byte)64, packetHeader); - Assert.AreEqual(1337u, commandID); - Assert.AreEqual(4 + data.Length + 1 + 1, dataLength); - - Assert.IsTrue(cb.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b)); - Assert.AreEqual((byte)0, b); - - cb.Read(send, 0, dataLength, Constants.TCP_HEADER_SIZE); - - byte[] deserializeBuffer = new byte[128]; - /*int checksum2 = Serialization.Serialization.Deserialize( - send, 4, dataLength - 5, deserializeBuffer, out int bufferLength); - - Assert.AreEqual(checksum, checksum2); - - Assert.AreEqual(data.Length, bufferLength); - Assert.IsTrue(data.SequenceEqual(deserializeBuffer.Take(bufferLength)));*/ - } - } - - [TestMethod] - public unsafe void SerializeTcpNoResponseBigPayloadTest() - { - Random rnd = new Random(7576); - byte[] data = new byte[(1 << 12) + 200]; - - //rnd.NextBytes(data); - int expectedLength = data.Length + Math2.Ceiling(data.Length / 7.0f); - - fixed (byte* src = data) - { - Serialization.Serialization.SerializeTcp( - 1337u, src, data.Length, 0, EncryptionMode.None, CompressionMode.Lz4, out byte[] send, - out int size); - Assert.IsTrue(size < Constants.TCP_HEADER_SIZE + 4 + expectedLength + 1 + 1); - - CircularBuffer cb = new CircularBuffer(data.Length); - cb.Write(send, 0, size); - - Assert.IsTrue( - cb.PeekHeader( - 0, out byte packetHeader, out uint commandID, out int dataLength, out ushort checksum)); - - Assert.AreEqual((byte)8, packetHeader); - Assert.AreEqual(1337u, commandID); - Assert.IsTrue(4 + data.Length + 1 + 1 > dataLength); - - Assert.IsTrue(cb.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b)); - Assert.AreEqual((byte)0, b); - - cb.Read(send, 0, dataLength, Constants.TCP_HEADER_SIZE); - - byte[] deserializeBuffer = new byte[128]; - /*int checksum2 = Serialization.Serialization.Deserialize( - send, 4, dataLength - 5, deserializeBuffer, out int bufferLength); - - Assert.AreEqual(27, bufferLength); - - Assert.AreEqual(checksum, checksum2); - Assert.AreEqual(data.Length, BitConverter.ToInt32(send, 0));*/ - } - } - } -} \ No newline at end of file diff --git a/Exomia.Network.sln b/Exomia.Network.sln index f9016f6..3734ad4 100644 --- a/Exomia.Network.sln +++ b/Exomia.Network.sln @@ -3,10 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27428.2015 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Exomia.Network", "Exomia.Network\Exomia.Network.csproj", "{4A17DCCB-D3DE-48DF-97A7-F46936096CA6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exomia.Network", "Exomia.Network\Exomia.Network.csproj", "{4A17DCCB-D3DE-48DF-97A7-F46936096CA6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Exomia.Network.UnitTest", "Exomia.Network.UnitTest\Exomia.Network.UnitTest.csproj", "{C70AE613-9A81-4063-8F4A-6F09F145EE85}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Example", "Example", "{4E24C133-E6A0-4BE2-9F75-8DC1827A7361}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Client", "Example.Client\Example.Client.csproj", "{90A9560E-16E9-4008-B061-B5827AF69880}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Server", "Example.Server\Example.Server.csproj", "{7E2A2465-B98E-42B8-B8AB-DE890F2406BF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,10 +47,38 @@ Global {C70AE613-9A81-4063-8F4A-6F09F145EE85}.Release|x64.Build.0 = Release|Any CPU {C70AE613-9A81-4063-8F4A-6F09F145EE85}.Release|x86.ActiveCfg = Release|Any CPU {C70AE613-9A81-4063-8F4A-6F09F145EE85}.Release|x86.Build.0 = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|x64.ActiveCfg = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|x64.Build.0 = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|x86.ActiveCfg = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Debug|x86.Build.0 = Debug|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|Any CPU.Build.0 = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|x64.ActiveCfg = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|x64.Build.0 = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|x86.ActiveCfg = Release|Any CPU + {90A9560E-16E9-4008-B061-B5827AF69880}.Release|x86.Build.0 = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|x64.ActiveCfg = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|x64.Build.0 = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|x86.ActiveCfg = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Debug|x86.Build.0 = Debug|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|Any CPU.Build.0 = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|x64.ActiveCfg = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|x64.Build.0 = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|x86.ActiveCfg = Release|Any CPU + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {90A9560E-16E9-4008-B061-B5827AF69880} = {4E24C133-E6A0-4BE2-9F75-8DC1827A7361} + {7E2A2465-B98E-42B8-B8AB-DE890F2406BF} = {4E24C133-E6A0-4BE2-9F75-8DC1827A7361} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4676E067-560B-4A5F-A7E7-9698C96317D0} EndGlobalSection diff --git a/Exomia.Network/AssemblyInfo.cs b/Exomia.Network/AssemblyInfo.cs new file mode 100644 index 0000000..861e76e --- /dev/null +++ b/Exomia.Network/AssemblyInfo.cs @@ -0,0 +1,13 @@ +#region License + +// Copyright (c) 2018-2019, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Exomia.Network.UnitTest")] \ No newline at end of file diff --git a/Exomia.Network/ClientBase.cs b/Exomia.Network/ClientBase.cs index 494e73c..7221a27 100644 --- a/Exomia.Network/ClientBase.cs +++ b/Exomia.Network/ClientBase.cs @@ -70,6 +70,16 @@ public abstract class ClientBase : IClient /// public event Action Ping; + /// + /// The big data handler. + /// + private protected readonly BigDataHandler _bigDataHandler; + + /// + /// Size of the maximum packet. + /// + protected readonly ushort _maxPacketSize; + /// /// The client socket. /// @@ -80,6 +90,16 @@ public abstract class ClientBase : IClient /// private protected byte _state; + /// + /// The compression mode. + /// + protected CompressionMode _compressionMode = CompressionMode.Lz4; + + /// + /// The encryption mode. + /// + private protected EncryptionMode _encryptionMode = EncryptionMode.None; + /// /// The connect checksum. /// @@ -125,6 +145,11 @@ public abstract class ClientBase : IClient /// private string _serverAddress; + /// + /// Identifier for the packet. + /// + private int _packetID; + /// /// Port. /// @@ -150,8 +175,11 @@ public string ServerAddress /// /// Initializes a new instance of the class. /// - private protected ClientBase() + /// Size of the maximum packet. + private protected ClientBase(ushort maxPacketSize) { + _maxPacketSize = maxPacketSize; + _clientSocket = null; _dataReceivedCallbacks = new Dictionary(INITIAL_QUEUE_SIZE); _taskCompletionSources = @@ -159,7 +187,10 @@ private protected ClientBase() _lockTaskCompletionSources = new SpinLock(Debugger.IsAttached); _dataReceivedCallbacksLock = new SpinLock(Debugger.IsAttached); - _responseID = 1; + + _responseID = 1; + + _bigDataHandler = new BigDataHandler(); Random rnd = new Random((int)DateTime.UtcNow.Ticks); rnd.NextBytes(_connectChecksum); @@ -180,8 +211,6 @@ public bool Connect(IPAddress[] ipAddresses, int port, int timeout = 10) { Disconnect(DisconnectReason.Graceful); - _port = port; - _manuelResetEvent.Reset(); if (TryCreateSocket(out _clientSocket)) @@ -200,6 +229,7 @@ public bool Connect(IPAddress[] ipAddresses, int port, int timeout = 10) ReceiveAsync(); if (SendConnect() == SendError.None) { + _port = port; return _manuelResetEvent.WaitOne(timeout * 1000); } } @@ -375,34 +405,41 @@ private protected unsafe void DeserializeData(uint commandID, #region Add & Remove /// - /// add a command. + /// add commands deserializers. /// - /// command id. - /// . - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// + /// The deserialize handler. + /// A variable-length parameters list containing command ids. /// /// Thrown when one or more required arguments /// are null. /// - public void AddCommand(uint commandID, DeserializePacketHandler deserialize) + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// + public void AddCommand(DeserializePacketHandler deserialize, params uint[] commandIDs) { - if (commandID > Constants.USER_COMMAND_LIMIT) - { - throw new ArgumentOutOfRangeException( - $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); - } + if (commandIDs == null) { throw new ArgumentNullException(nameof(commandIDs)); } if (deserialize == null) { throw new ArgumentNullException(nameof(deserialize)); } + bool lockTaken = false; try { _dataReceivedCallbacksLock.Enter(ref lockTaken); - if (!_dataReceivedCallbacks.TryGetValue(commandID, out ClientEventEntry buffer)) + + foreach (uint commandID in commandIDs) { - buffer = new ClientEventEntry(deserialize); - _dataReceivedCallbacks.Add(commandID, buffer); + if (commandID > Constants.USER_COMMAND_LIMIT) + { + throw new ArgumentOutOfRangeException( + $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); + } + if (!_dataReceivedCallbacks.TryGetValue( + commandID, out ClientEventEntry buffer)) + { + buffer = new ClientEventEntry(deserialize); + _dataReceivedCallbacks.Add(commandID, buffer); + } } } finally @@ -412,37 +449,45 @@ public void AddCommand(uint commandID, DeserializePacketHandler deserial } /// - /// remove a command. + /// Removes the commands described by commandIDs. /// - /// command id. + /// A variable-length parameters list containing command ids. + /// + /// True if at least one command is removed, false otherwise. + /// /// /// Thrown when one or more arguments are outside /// the required range. /// - public void RemoveCommand(uint commandID) + public bool RemoveCommands(params uint[] commandIDs) { - if (commandID > Constants.USER_COMMAND_LIMIT) - { - throw new ArgumentOutOfRangeException( - $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); - } + bool removed = false; bool lockTaken = false; try { _dataReceivedCallbacksLock.Enter(ref lockTaken); - _dataReceivedCallbacks.Remove(commandID); + foreach (uint commandID in commandIDs) + { + if (commandID > Constants.USER_COMMAND_LIMIT) + { + throw new ArgumentOutOfRangeException( + $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); + } + removed |= _dataReceivedCallbacks.Remove(commandID); + } } finally { if (lockTaken) { _dataReceivedCallbacksLock.Exit(false); } } + return removed; } /// /// add a data received callback. /// - /// command id. - /// callback. + /// Identifier for the command. + /// The callback. /// /// Thrown when one or more arguments are outside /// the required range. @@ -462,30 +507,32 @@ public void AddDataReceivedCallback(uint commandID, DataReceivedHandler callback throw new ArgumentOutOfRangeException( $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); } + if (callback == null) { throw new ArgumentNullException(nameof(callback)); } - ClientEventEntry buffer; - bool lockTaken = false; + + bool lockTaken = false; try { _dataReceivedCallbacksLock.Enter(ref lockTaken); - if (!_dataReceivedCallbacks.TryGetValue(commandID, out buffer)) + if (!_dataReceivedCallbacks.TryGetValue(commandID, out ClientEventEntry buffer)) { throw new Exception( - $"Invalid parameter '{nameof(commandID)}'! Use 'AddCommand(uint, DeserializeData)' first."); + $"Invalid parameter '{nameof(commandID)}'! Use 'AddCommand(DeserializeData, params uint[])' first."); } + + buffer.Add(callback); } finally { if (lockTaken) { _dataReceivedCallbacksLock.Exit(false); } } - buffer.Add(callback); } /// /// remove a data received callback. /// - /// command id. - /// DataReceivedHandler. + /// Identifier for the command. + /// The callback. /// /// Thrown when one or more arguments are outside /// the required range. @@ -501,7 +548,9 @@ public void RemoveDataReceivedCallback(uint commandID, DataReceivedHandler callb throw new ArgumentOutOfRangeException( $"{nameof(commandID)} is restricted to 0 - {Constants.USER_COMMAND_LIMIT}"); } + if (callback == null) { throw new ArgumentNullException(nameof(callback)); } + if (_dataReceivedCallbacks.TryGetValue(commandID, out ClientEventEntry buffer)) { buffer.Remove(callback); @@ -512,22 +561,53 @@ public void RemoveDataReceivedCallback(uint commandID, DataReceivedHandler callb #region Send - /// - /// Begins send data. - /// - /// command id. - /// The data. - /// The offset. - /// The length. - /// Identifier for the response. - /// - /// A SendError. - /// - private protected abstract SendError BeginSendData(uint commandID, - byte[] data, - int offset, - int length, - uint responseID); + private protected abstract unsafe SendError BeginSendData(int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length); + + private unsafe SendError BeginSendData(uint commandID, + byte[] data, + int offset, + int length, + uint responseID) + { + if (_clientSocket == null) { return SendError.Invalid; } + if ((_state & SEND_FLAG) == SEND_FLAG) + { + fixed (byte* src = data) + { + if (length > _maxPacketSize) + { + int packetID = Interlocked.Increment(ref _packetID); + int chunkOffset = 0; + int chunkLength = length; + while (chunkLength > _maxPacketSize) + { + SendError se = BeginSendData( + packetID, commandID, responseID, + src + offset + chunkOffset, _maxPacketSize, chunkOffset, length); + if (se != SendError.None) + { + return se; + } + chunkLength -= _maxPacketSize; + chunkOffset += _maxPacketSize; + } + return BeginSendData( + packetID, commandID, responseID, + src + offset + chunkOffset, _maxPacketSize, chunkOffset, length); + } + return BeginSendData( + 0, commandID, responseID, + src + offset, length, 0, length); + } + } + return SendError.Invalid; + } /// public SendError Send(uint commandID, byte[] data, int offset, int length) diff --git a/Exomia.Network/Constants.cs b/Exomia.Network/Constants.cs index d75c050..16a2b37 100644 --- a/Exomia.Network/Constants.cs +++ b/Exomia.Network/Constants.cs @@ -8,10 +8,6 @@ #endregion -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Exomia.Network.UnitTest")] - namespace Exomia.Network { /// @@ -19,34 +15,89 @@ namespace Exomia.Network /// static class Constants { + /// + /// The safety payload offset. + /// + internal const int SAFETY_PAYLOAD_OFFSET = 20; + /// /// Size of the TCP header. /// internal const int TCP_HEADER_SIZE = 7; /// - /// The TCP packet size maximum. + /// The TCP header offset. /// - internal const ushort TCP_PACKET_SIZE_MAX = 65535 - TCP_HEADER_SIZE - 8; + internal const int TCP_HEADER_OFFSET = TCP_HEADER_SIZE + SAFETY_PAYLOAD_OFFSET; /// - /// The zero byte. + /// The TCP packet size maximum. /// - internal const byte ZERO_BYTE = 0; + internal const ushort TCP_PACKET_SIZE_MAX = 65535 - TCP_HEADER_OFFSET; /// /// Size of the UDP header. /// internal const int UDP_HEADER_SIZE = 5; + /// + /// The UDP header offset. + /// + internal const int UDP_HEADER_OFFSET = TCP_HEADER_SIZE + SAFETY_PAYLOAD_OFFSET; + /// /// The UDP packet size maximum. /// - internal const ushort UDP_PACKET_SIZE_MAX = 65535 - UDP_HEADER_SIZE - 8; + internal const ushort UDP_PACKET_SIZE_MAX = 65535 - UDP_HEADER_OFFSET; /// /// The user command limit. /// internal const uint USER_COMMAND_LIMIT = 65500; + + /// + /// The command identifier shift. + /// + internal const int COMMAND_ID_SHIFT = 16; + + /// + /// The data length mask. + /// + internal const int DATA_LENGTH_MASK = 0xFFFF; + + /// + /// LENGTH_THRESHOLD 4096. + /// + internal const int LENGTH_THRESHOLD = 1 << 12; + + /// + /// The response bit. + /// + internal const byte RESPONSE_1_BIT = 1 << 6; + + /// + /// The is chunked bit. + /// + internal const byte IS_CHUNKED_1_BIT = 1 << 7; + + /// + /// The compressed mode mask. + /// + internal const byte COMPRESSED_MODE_MASK = 0b0011_1000; + + /// + /// The response bit mask. + /// + internal const uint RESPONSE_BIT_MASK = 0b0100_0000; + + /// + /// The is chunked bit mask. + /// + internal const uint IS_CHUNKED_BIT_MASK = 0b1000_0000; + + /// + /// The zero byte. + /// + internal const byte ZERO_BYTE = 0; } } \ No newline at end of file diff --git a/Exomia.Network/Encoding/PayloadEncoding.cs b/Exomia.Network/Encoding/PayloadEncoding.cs index ecb2b2c..189901a 100644 --- a/Exomia.Network/Encoding/PayloadEncoding.cs +++ b/Exomia.Network/Encoding/PayloadEncoding.cs @@ -58,57 +58,57 @@ static unsafe class PayloadEncoding private static readonly uint s_h0 = H0 ^ Math2.R1(H0, 12); /// - /// Decodes the data. + /// Encodes the data. /// - /// [in,out] If non-null, the decode source. - /// The offset. + /// The data. /// The length. /// The buffer. /// [out] Length of the buffer. /// /// An ushort. /// - internal static ushort Decode(byte* src, int offset, int length, byte[] buffer, out int bufferLength) + internal static ushort Encode(byte* data, int length, byte* buffer, out int bufferLength) { - bufferLength = length - Math2.Ceiling(length / 8.0); + bufferLength = length + Math2.Ceiling(length / 7.0f); uint checksum = s_h0; - int o1 = 0; - fixed (byte* dest = buffer) + while (length > 7) { - while (offset + 8 < length) - { - Decode(&checksum, dest, o1, src, offset, 8); - o1 += 7; - offset += 8; - } - Decode(&checksum, dest, o1, src, offset, length - offset); + Encode(&checksum, buffer, data, 7); + buffer += 8; + data += 7; + length -= 7; } + Encode(&checksum, buffer, data, length); return (ushort)(CONE | ((ushort)checksum ^ (checksum >> 16))); } /// - /// Encodes the data. + /// Decodes the data. /// - /// The data. + /// [in,out] If non-null, the decode source. /// The length. /// The buffer. /// [out] Length of the buffer. /// /// An ushort. /// - internal static ushort Encode(byte* data, int length, byte* buffer, out int bufferLength) + internal static ushort Decode(byte* src, int length, byte[] buffer, out int bufferLength) { - bufferLength = length + Math2.Ceiling(length / 7.0f); + bufferLength = length - Math2.Ceiling(length / 8.0); uint checksum = s_h0; - while (length > 7) + fixed (byte* dest = buffer) { - Encode(&checksum, buffer, data, 7); - buffer += 8; - data += 7; - length -= 7; + byte* d = dest; + while (length > 8) + { + Decode(&checksum, d, src, 8); + d += 7; + src += 8; + length -= 8; + } + Decode(&checksum, d, src, length); } - Encode(&checksum, buffer, data, length); return (ushort)(CONE | ((ushort)checksum ^ (checksum >> 16))); } @@ -140,18 +140,17 @@ private static void Encode(uint* checksum, byte* buffer, byte* data, int size) /// /// [in,out] If non-null, the checksum. /// [in,out] If non-null, destination for the. - /// The first int. /// [in,out] If non-null, source for the. /// The second int. /// The size. - private static void Decode(uint* checksum, byte* dest, int o1, byte* src, int o2, int size) + private static void Decode(uint* checksum, byte* dest, byte* src, int size) { - byte b = *((src + o2 + size) - 1); + byte b = *((src + size) - 1); for (int i = 0; i < size - 1; ++i) { - byte d = (byte)(((b & (MASK2 >> i)) << (i + 1)) | (*(src + o2 + i) & MASK1)); - *(dest + o1 + i) = d; - *checksum ^= d + C0; + byte d = (byte)(((b & (MASK2 >> i)) << (i + 1)) | (*(src + i) & MASK1)); + *(dest + i) = d; + *checksum ^= d + C0; } *checksum += Math2.R1(b, 23) + C1; } diff --git a/Exomia.Network/Exomia.Network.csproj b/Exomia.Network/Exomia.Network.csproj index c15868f..b1b7711 100644 --- a/Exomia.Network/Exomia.Network.csproj +++ b/Exomia.Network/Exomia.Network.csproj @@ -4,7 +4,7 @@ exomia tcp / udp client and server Copyright © $([System.DateTime]::Now.Year) exomia - 1.4.3.0 + 1.5.0.0 https://raw.githubusercontent.com/exomia/network/master/LICENSE https://github.com/exomia/network true diff --git a/Exomia.Network/Lib/ServerClientEventEntry.cs b/Exomia.Network/Lib/ServerClientEventEntry.cs index 0472d4c..24f6e11 100644 --- a/Exomia.Network/Lib/ServerClientEventEntry.cs +++ b/Exomia.Network/Lib/ServerClientEventEntry.cs @@ -33,8 +33,8 @@ sealed class ServerClientEventEntry /// The deserialize. public ServerClientEventEntry(DeserializePacketHandler deserialize) { - _dataReceived = new Event>(); _deserialize = deserialize; + _dataReceived = new Event>(); } /// diff --git a/Exomia.Network/Native/CircularBuffer.cs b/Exomia.Network/Native/CircularBuffer.cs index b154257..b9ad522 100644 --- a/Exomia.Network/Native/CircularBuffer.cs +++ b/Exomia.Network/Native/CircularBuffer.cs @@ -165,69 +165,31 @@ public void Clear() /// /// read a piece from the buffer. /// - /// destination array. - /// offset. + /// the destination array. + /// the offset. /// length. /// skip bytes. /// - /// a byte array. + /// An int. /// - public int Read(byte[] dest, int offset, int length, int skip) + public int Read(byte[] dst, int offset, int length, int skip) { - bool lockTaken = false; - try - { - _lock.Enter(ref lockTaken); - if (_count == 0) - { - return 0; - } - - if (skip + length > _count) - { - length = _count - skip; - } - - fixed (byte* d = dest) - { - if (_tail + skip + length < _capacity) - { - Mem.Cpy(d + offset, _ptr + _tail + skip, length); - } - else if (_tail + skip < _capacity) - { - int l1 = _capacity - (_tail + skip); - Mem.Cpy(d + offset, _ptr + _tail + skip, l1); - Mem.Cpy(d + offset + l1, _ptr, length - l1); - } - else - { - Mem.Cpy(d + offset, _ptr + ((_tail + skip) & _mask), length); - } - } - - _tail = (_tail + length) & _mask; - _count -= skip + length; - - return length; - } - finally + fixed (byte* ptr = dst) { - if (lockTaken) { _lock.Exit(false); } + return Read(ptr + offset, length, skip); } } /// /// read a piece from the buffer. /// - /// [in,out] destination array. - /// offset. + /// [in,out] the destination array. /// length. /// skip bytes. /// /// An int. /// - public int Read(byte* dest, int offset, int length, int skip) + public int Read(byte* dst, int length, int skip) { bool lockTaken = false; try @@ -245,17 +207,17 @@ public int Read(byte* dest, int offset, int length, int skip) if (_tail + skip + length < _capacity) { - Mem.Cpy(dest + offset, _ptr + _tail + skip, length); + Mem.Cpy(dst, _ptr + _tail + skip, length); } else if (_tail + skip < _capacity) { int l1 = _capacity - (_tail + skip); - Mem.Cpy(dest + offset, _ptr + _tail + skip, l1); - Mem.Cpy(dest + offset + l1, _ptr, length - l1); + Mem.Cpy(dst, _ptr + _tail + skip, l1); + Mem.Cpy(dst + l1, _ptr, length - l1); } else { - Mem.Cpy(dest + offset, _ptr + ((_tail + skip) & _mask), length); + Mem.Cpy(dst, _ptr + ((_tail + skip) & _mask), length); } _tail = (_tail + skip + length) & _mask; @@ -272,66 +234,31 @@ public int Read(byte* dest, int offset, int length, int skip) /// /// peek a piece from the buffer. /// - /// destination array. - /// offset. - /// length you want to read from the buffer. - /// skip bytes. + /// the destination array. + /// the offset. + /// the length you want to read from the buffer. + /// the bytes to skip. /// /// a byte array. /// - public int Peek(byte[] dest, int offset, int length, int skip) + public int Peek(byte[] dst, int offset, int length, int skip) { - bool lockTaken = false; - try + fixed (byte* ptr = dst) { - _lock.Enter(ref lockTaken); - if (_count == 0) - { - return 0; - } - - if (skip + length > _count) - { - length = _count - skip; - } - - fixed (byte* d = dest) - { - if (_tail + skip + length < _capacity) - { - Mem.Cpy(d + offset, _ptr + _tail + skip, length); - } - else if (_tail + skip < _capacity) - { - int l1 = _capacity - (_tail + skip); - Mem.Cpy(d + offset, _ptr + _tail + skip, l1); - Mem.Cpy(d + offset + l1, _ptr, length - l1); - } - else - { - Mem.Cpy(d + offset, _ptr + ((_tail + skip) & _mask), length); - } - } - - return length; - } - finally - { - if (lockTaken) { _lock.Exit(false); } + return Peek(ptr + offset, length, skip); } } /// /// peek a piece from the buffer. /// - /// [in,out] destination array. - /// offset. - /// length you want to read from the buffer. - /// skip bytes. + /// [in,out] destination array. + /// the length you want to read from the buffer. + /// the bytes to skip. /// /// a byte array. /// - public int Peek(byte* dest, int offset, int length, int skip) + public int Peek(byte* dst, int length, int skip) { bool lockTaken = false; try @@ -349,17 +276,17 @@ public int Peek(byte* dest, int offset, int length, int skip) if (_tail + skip + length < _capacity) { - Mem.Cpy(dest + offset, _ptr + _tail + skip, length); + Mem.Cpy(dst, _ptr + _tail + skip, length); } else if (_tail + skip < _capacity) { int l1 = _capacity - (_tail + skip); - Mem.Cpy(dest + offset, _ptr + _tail + skip, l1); - Mem.Cpy(dest + offset + l1, _ptr, length - l1); + Mem.Cpy(dst, _ptr + _tail + skip, l1); + Mem.Cpy(dst + l1, _ptr, length - l1); } else { - Mem.Cpy(dest + offset, _ptr + ((_tail + skip) & _mask), length); + Mem.Cpy(dst, _ptr + ((_tail + skip) & _mask), length); } return length; @@ -371,12 +298,12 @@ public int Peek(byte* dest, int offset, int length, int skip) } /// - /// peek a single byte from the buffer. + /// Peek byte. /// - /// offset. - /// [out] out byte. + /// the offset. + /// [out] The out byte to process. /// - /// true if the peek was successful; false otherwise. + /// True if it succeeds, false if it fails. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool PeekByte(int offset, out byte b) @@ -400,15 +327,15 @@ public bool PeekByte(int offset, out byte b) } /// - /// peek a single byte from the buffer. + /// Peek header. /// /// skip bytes. - /// [out] packetHeader. - /// [out] command id. - /// [out] data length. - /// [out]. + /// [out] The packet header. + /// [out] Identifier for the command. + /// [out] Length of the data. + /// [out] The checksum. /// - /// a byte array. + /// True if it succeeds, false if it fails. /// public bool PeekHeader(int skip, out byte packetHeader, @@ -429,34 +356,12 @@ public bool PeekHeader(int skip, return false; } - // 8bit - // - // | UNUSED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | - // | 7 | 6 | 5 4 3 | 2 1 0 | - // | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE - // --------------------------------------------------------------------------------------------------------------------- - // | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 - // | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 - // | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 - // | 1 | 0 | 0 0 0 | 0 0 0 | UNUSED_BIT_MASK 0b10000000 - - // 32bit - // - // | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | - // | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | - // | VR: 0-65535 | VR: 0-65535 | VR = VALUE RANGE - // -------------------------------------------------------------------------------------------------------------------------------- - // | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | DATA_LENGTH_MASK 0xFFFF - // | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | COMMANDID_MASK 0xFFFF0000 - - // 16bit - CHECKSUM - if (_tail + skip + Constants.TCP_HEADER_SIZE < _capacity) { packetHeader = *(_ptr + _tail + skip); uint h2 = *(uint*)(_ptr + _tail + skip + 1); - commandID = h2 >> Serialization.Serialization.COMMAND_ID_SHIFT; - dataLength = (int)(h2 & Serialization.Serialization.DATA_LENGTH_MASK); + commandID = h2 >> Constants.COMMAND_ID_SHIFT; + dataLength = (int)(h2 & Constants.DATA_LENGTH_MASK); checksum = *(ushort*)(_ptr + _tail + skip + 5); } else if (_tail + skip < _capacity) @@ -466,8 +371,8 @@ public bool PeekHeader(int skip, | (*(_ptr + ((_tail + skip + 3) & _mask)) << 16) | (*(_ptr + ((_tail + skip + 2) & _mask)) << 8) | *(_ptr + ((_tail + skip + 1) & _mask))); - commandID = h2 >> Serialization.Serialization.COMMAND_ID_SHIFT; - dataLength = (int)(h2 & Serialization.Serialization.DATA_LENGTH_MASK); + commandID = h2 >> Constants.COMMAND_ID_SHIFT; + dataLength = (int)(h2 & Constants.DATA_LENGTH_MASK); checksum = (ushort)( (*(_ptr + ((_tail + skip + 6) & _mask)) << 8) | *(_ptr + ((_tail + skip + 5) & _mask))); @@ -476,8 +381,8 @@ public bool PeekHeader(int skip, { packetHeader = *(_ptr + ((_tail + skip) & _mask)); uint h2 = *(uint*)(_ptr + ((_tail + skip + 1) & _mask)); - commandID = h2 >> Serialization.Serialization.COMMAND_ID_SHIFT; - dataLength = (int)(h2 & Serialization.Serialization.DATA_LENGTH_MASK); + commandID = h2 >> Constants.COMMAND_ID_SHIFT; + dataLength = (int)(h2 & Constants.DATA_LENGTH_MASK); checksum = *(ushort*)(_ptr + ((_tail + skip + 5) & _mask)); } @@ -492,7 +397,7 @@ public bool PeekHeader(int skip, /// /// skips until a specified byte is found. /// - /// offset. + /// the offset. /// the value to compare with. /// /// true if the value was found; false otherwise @@ -529,7 +434,7 @@ public bool SkipUntil(int offset, byte value) /// /// skips a specified count. /// - /// count to skip. + /// the count to skip. public void Skip(int count) { bool lockTaken = false; @@ -550,63 +455,35 @@ public void Skip(int count) } /// - /// write a piece of data into the buffer attention: if you write to much unread data will be - /// overridden. + /// write a piece of data into the buffer + /// attention: + /// if you write to much unread data will be overridden. /// - /// source array. + /// the value to compare with. /// offset. - /// length. + /// the length. /// /// the bytes written to the buffer. /// public int Write(byte[] value, int offset, int length) { - bool lockTaken = false; - try + fixed (byte* src = value) { - _lock.Enter(ref lockTaken); - - if (_count + length > _capacity) - { - length = _capacity - _count; - } - - fixed (byte* src = value) - { - if (_head + length < _capacity) - { - Mem.Cpy(_ptr + _head, src + offset, length); - } - else - { - int l1 = _capacity - _head; - Mem.Cpy(_ptr + _head, src + offset, l1); - Mem.Cpy(_ptr, src + offset + l1, length - l1); - } - } - - _head = (_head + length) & _mask; - _count += length; - - return length; - } - finally - { - if (lockTaken) { _lock.Exit(false); } + return Write(src + offset, length); } } /// - /// write a piece of data into the buffer attention: if you write to much unread data will be - /// overridden. + /// write a piece of data into the buffer + /// attention: + /// if you write to much unread data will be overridden. /// - /// [in,out] source array. - /// source offset. - /// length. + /// [in,out] the source array. + /// the length. /// /// the bytes written to the buffer. /// - public int Write(byte* src, int offset, int length) + public int Write(byte* src, int length) { bool lockTaken = false; try @@ -620,13 +497,13 @@ public int Write(byte* src, int offset, int length) if (_head + length < _capacity) { - Mem.Cpy(_ptr + _head, src + offset, length); + Mem.Cpy(_ptr + _head, src, length); } else { int l1 = _capacity - _head; - Mem.Cpy(_ptr + _head, src + offset, l1); - Mem.Cpy(_ptr, src + offset + l1, length - l1); + Mem.Cpy(_ptr + _head, src, l1); + Mem.Cpy(_ptr, src + l1, length - l1); } _head = (_head + length) & _mask; diff --git a/Exomia.Network/Serialization/Serialization.Tcp.cs b/Exomia.Network/Serialization/Serialization.Tcp.cs index 46101c6..61bfaed 100644 --- a/Exomia.Network/Serialization/Serialization.Tcp.cs +++ b/Exomia.Network/Serialization/Serialization.Tcp.cs @@ -12,156 +12,236 @@ using System.Runtime.CompilerServices; using Exomia.Network.Buffers; using Exomia.Network.Encoding; -using Exomia.Network.Lib; +using Exomia.Network.Native; using K4os.Compression.LZ4; namespace Exomia.Network.Serialization { /// - /// A serialization. + /// A TCP serialization helper class. /// + /// + /// 8bit + /// | IS_CHUNKED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | + /// | 7 | 6 | 5 4 3 | 2 1 0 | + /// | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE + /// ----------------------------------------------------------------------------------------------------------------------- + /// | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 + /// | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 + /// | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 + /// | 1 | 0 | 0 0 0 | 0 0 0 | IS_CHUNKED_BIT_MASK 0b10000000 + /// 32bit + /// | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | + /// | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | + /// | VR: 0-65535 | VR: 0-65535 | VR = VALUE + /// RANGE + /// -------------------------------------------------------------------------------------------------------------------------------- + /// | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | + /// DATA_LENGTH_MASK 0xFFFF + /// | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | + /// COMMANDID_MASK 0xFFFF0000 + /// 16bit - CHECKSUM + /// static unsafe partial class Serialization { /// /// Serialize TCP. /// + /// Identifier for the packet. /// Identifier for the command. - /// [in,out] If non-null, source for the. - /// The length. /// Identifier for the response. - /// The encryption mode. - /// The compression mode. - /// [in,out] If non-null, destination for the. - /// [out] The size. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SerializeTcp(uint commandID, - byte* src, - int length, - uint responseID, - EncryptionMode encryptionMode, - CompressionMode compressionMode, - out byte[] dst, - out int size) - { - dst = ByteArrayPool.Rent(Constants.TCP_HEADER_SIZE + 9 + length + Math2.Ceiling(length / 7.0f)); - fixed (byte* ptr = dst) - { - SerializeTcp(commandID, src, length, responseID, encryptionMode, compressionMode, ptr, out size); - } - } - - /// - /// Serialize TCP. - /// - /// Identifier for the command. /// [in,out] If non-null, source for the. + /// [in,out] If non-null, destination for the. + /// Length of the chunk. + /// The chunk offset. /// The length. - /// Identifier for the response. /// The encryption mode. /// The compression mode. - /// [in,out] If non-null, destination for the. - /// [out] The size. /// /// Thrown when one or more arguments are outside /// the required range. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SerializeTcp(uint commandID, - byte* src, - int length, - uint responseID, - EncryptionMode encryptionMode, - CompressionMode compressionMode, - byte* dst, - out int size) + internal static int SerializeTcp(int packetId, + uint commandID, + uint responseID, + byte* src, + byte* dst, + int chunkLength, + int chunkOffset, + int length, + EncryptionMode encryptionMode, + CompressionMode compressionMode) { - // 8bit - // - // | UNUSED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | - // | 7 | 6 | 5 4 3 | 2 1 0 | - // | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE - // --------------------------------------------------------------------------------------------------------------------- - // | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 - // | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 - // | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 - // | 1 | 0 | 0 0 0 | 0 0 0 | UNUSED_BIT_MASK 0b10000000 - - // 32bit - // - // | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | - // | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | - // | VR: 0-65535 | VR: 0-65535 | VR = VALUE RANGE - // -------------------------------------------------------------------------------------------------------------------------------- - // | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | DATA_LENGTH_MASK 0xFFFF - // | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | COMMANDID_MASK 0xFFFF0000 - - // 16bit - CHECKSUM - *dst = (byte)encryptionMode; - int offset; - if (responseID == 0) + int offset = 0; + if (chunkLength != length) { - offset = 0; + *dst |= Constants.IS_CHUNKED_1_BIT; + *(int*)(dst + Constants.TCP_HEADER_SIZE) = packetId; + *(int*)(dst + Constants.TCP_HEADER_SIZE + 4) = chunkOffset; + *(int*)(dst + Constants.TCP_HEADER_SIZE + 8) = length; + offset = 12; } - else + + if (responseID != 0) { - offset = 4; - *dst |= RESPONSE_1_BIT; - *(uint*)(dst + 7) = responseID; + *dst |= Constants.RESPONSE_1_BIT; + *(uint*)(dst + Constants.TCP_HEADER_SIZE + offset) = responseID; + offset += 4; } int l; ushort checksum; - if (length >= LENGTH_THRESHOLD && compressionMode != CompressionMode.None) + if (chunkLength >= Constants.LENGTH_THRESHOLD && compressionMode != CompressionMode.None) { - byte[] buffer = ByteArrayPool.Rent(length); + byte[] buffer = ByteArrayPool.Rent(chunkLength); fixed (byte* bPtr = buffer) { int s; switch (compressionMode) { case CompressionMode.Lz4: - s = LZ4Codec.Encode(src, length, bPtr, length); + s = LZ4Codec.Encode(src, chunkLength, bPtr, chunkLength); break; default: throw new ArgumentOutOfRangeException( nameof(compressionMode), compressionMode, "Not supported!"); } - if (s > Constants.TCP_PACKET_SIZE_MAX) - { - ByteArrayPool.Return(buffer); - throw new ArgumentOutOfRangeException( - $"packet size of {Constants.TCP_PACKET_SIZE_MAX} exceeded (s: {s})"); - } - if (s > 0) + if (s > 0 && s < chunkLength) { checksum = PayloadEncoding.Encode(bPtr, s, dst + Constants.TCP_HEADER_SIZE + offset + 4, out l); ByteArrayPool.Return(buffer); - size = Constants.TCP_HEADER_SIZE + offset + 5 + l; - *dst |= (byte)compressionMode; *(uint*)(dst + 1) = - ((uint)(l + offset + 5) & DATA_LENGTH_MASK) | (commandID << COMMAND_ID_SHIFT); + ((uint)(s + offset + 5) & Constants.DATA_LENGTH_MASK) | + (commandID << Constants.COMMAND_ID_SHIFT); *(ushort*)(dst + 5) = checksum; - *(int*)(dst + offset + 7) = length; - *(int*)(dst + Constants.TCP_HEADER_SIZE + l + offset + 4) = Constants.ZERO_BYTE; - - return; + *(int*)(dst + Constants.TCP_HEADER_SIZE + offset) = chunkLength; + *(int*)(dst + Constants.TCP_HEADER_SIZE + offset + 4 + l) = Constants.ZERO_BYTE; + return Constants.TCP_HEADER_SIZE + offset + s + 5; } ByteArrayPool.Return(buffer); } } - checksum = PayloadEncoding.Encode(src, length, dst + Constants.TCP_HEADER_SIZE + offset, out l); - size = Constants.TCP_HEADER_SIZE + offset + 1 + l; - + checksum = PayloadEncoding.Encode(src, chunkLength, dst + Constants.TCP_HEADER_SIZE + offset, out l); *(uint*)(dst + 1) = - ((uint)(l + offset + 1) & DATA_LENGTH_MASK) | (commandID << COMMAND_ID_SHIFT); + ((uint)(l + offset + 1) & Constants.DATA_LENGTH_MASK) | (commandID << Constants.COMMAND_ID_SHIFT); *(ushort*)(dst + 5) = checksum; - *(int*)(dst + Constants.TCP_HEADER_SIZE + l + offset) = Constants.ZERO_BYTE; + *(int*)(dst + Constants.TCP_HEADER_SIZE + offset + l) = Constants.ZERO_BYTE; + + return Constants.TCP_HEADER_SIZE + offset + l + 1; + } + + internal static bool DeserializeTcp(CircularBuffer circularBuffer, + byte[] bufferWrite, + byte[] bufferRead, + int bytesTransferred, + BigDataHandler bigDataHandler, + out uint commandID, + out uint responseID, + out byte[] data, + out int dataLength) + { + int size = circularBuffer.Write(bufferWrite, 0, bytesTransferred); + while (circularBuffer.PeekHeader( + 0, out byte packetHeader, out commandID, out dataLength, out ushort checksum) + && dataLength <= circularBuffer.Count - Constants.TCP_HEADER_SIZE) + { + if (circularBuffer.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b) && + b == Constants.ZERO_BYTE) + { + fixed (byte* ptr = bufferRead) + { + circularBuffer.Read(ptr, dataLength, Constants.TCP_HEADER_SIZE); + if (size < bytesTransferred) + { + circularBuffer.Write(bufferWrite, size, bytesTransferred - size); + } + + responseID = 0; + int packetId = 0; + int chunkOffset = 0; + int length = 0; + int offset = 0; + if ((packetHeader & Constants.IS_CHUNKED_1_BIT) != 0) + { + packetId = *(int*)(ptr + offset); + chunkOffset = *(int*)(ptr + offset + 4); + length = *(int*)(ptr + offset + 8); + + offset += 12; + } + + if ((packetHeader & Constants.RESPONSE_BIT_MASK) != 0) + { + responseID = *(uint*)(ptr + offset); + offset += 4; + } + + CompressionMode compressionMode = + (CompressionMode)(packetHeader & Constants.COMPRESSED_MODE_MASK); + if (compressionMode != CompressionMode.None) + { + offset += 4; + } + + data = ByteArrayPool.Rent(dataLength); + if (PayloadEncoding.Decode( + ptr + offset, dataLength - offset - 1, data, out dataLength) == checksum) + { + switch (compressionMode) + { + case CompressionMode.None: + break; + case CompressionMode.Lz4: + int l = *(int*)((ptr + offset) - 4); + byte[] buffer = ByteArrayPool.Rent(l); + int s = LZ4Codec.Decode(data, 0, dataLength, buffer, 0, l); + if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } + + ByteArrayPool.Return(data); + data = buffer; + dataLength = l; + break; + default: + throw new ArgumentOutOfRangeException( + nameof(CompressionMode), compressionMode, "Not supported!"); + } + + if ((packetHeader & Constants.IS_CHUNKED_1_BIT) != 0) + { + fixed (byte* dst = data) + { + data = bigDataHandler.Receive(packetId, dst, dataLength, chunkOffset, length); + } + if (data != null) + { + dataLength = data.Length; + return true; + } + return false; + } + + return true; + } + return false; + } + } + bool skipped = circularBuffer.SkipUntil(Constants.TCP_HEADER_SIZE, Constants.ZERO_BYTE); + if (size < bytesTransferred) + { + size += circularBuffer.Write(bufferWrite, size, bytesTransferred - size); + } + if (!skipped && !circularBuffer.SkipUntil(0, Constants.ZERO_BYTE)) { break; } + } + data = null; + responseID = 0; + return false; } } } \ No newline at end of file diff --git a/Exomia.Network/Serialization/Serialization.Udp.cs b/Exomia.Network/Serialization/Serialization.Udp.cs index f6e269c..e7de487 100644 --- a/Exomia.Network/Serialization/Serialization.Udp.cs +++ b/Exomia.Network/Serialization/Serialization.Udp.cs @@ -9,7 +9,6 @@ #endregion using System; -using System.Runtime.CompilerServices; using Exomia.Network.Buffers; using Exomia.Network.Native; using K4os.Compression.LZ4; @@ -17,175 +16,198 @@ namespace Exomia.Network.Serialization { /// - /// A serialization. + /// A UDP serialization helper class. /// + /// + /// 8bit + /// | IS_CHUNKED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | + /// | 7 | 6 | 5 4 3 | 2 1 0 | + /// | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE + /// ----------------------------------------------------------------------------------------------------------------------- + /// | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 + /// | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 + /// | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 + /// | 1 | 0 | 0 0 0 | 0 0 0 | IS_CHUNKED_BIT_MASK 0b10000000 + /// 32bit + /// | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | + /// | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | + /// | VR: 0-65535 | VR: 0-65535 | VR = VALUE + /// RANGE + /// -------------------------------------------------------------------------------------------------------------------------------- + /// | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | + /// DATA_LENGTH_MASK 0xFFFF + /// | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | + /// COMMANDID_MASK 0xFFFF0000 + /// static unsafe partial class Serialization { /// /// Serialize UDP. /// + /// Identifier for the packet. /// Identifier for the command. - /// [in,out] If non-null, source for the. - /// The length. /// Identifier for the response. - /// The encryption mode. - /// The compression mode. - /// [out] Destination for the. - /// [out] The size. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SerializeUdp(uint commandID, - byte* src, - int length, - uint responseID, - EncryptionMode encryptionMode, - CompressionMode compressionMode, - out byte[] dst, - out int size) - { - dst = ByteArrayPool.Rent(Constants.UDP_HEADER_SIZE + 8 + length); - fixed (byte* ptr = dst) - { - SerializeUdp(commandID, src, length, responseID, encryptionMode, compressionMode, ptr, out size); - } - } - - /// - /// Serialize UDP. - /// - /// Identifier for the command. /// [in,out] If non-null, source for the. + /// [in,out] If non-null, destination for the. + /// Length of the chunk. + /// The chunk offset. /// The length. - /// Identifier for the response. /// The encryption mode. /// The compression mode. - /// [out] Destination for the. - /// [out] The size. - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - internal static void SerializeUdp(uint commandID, - byte* src, - int length, - uint responseID, - EncryptionMode encryptionMode, - CompressionMode compressionMode, - byte* dst, - out int size) + /// + /// An int. + /// + /// Thrown when one or more arguments are outside the required range. + internal static int SerializeUdp(int packetId, + uint commandID, + uint responseID, + byte* src, + byte* dst, + int chunkLength, + int chunkOffset, + int length, + EncryptionMode encryptionMode, + CompressionMode compressionMode) { - // 8bit - // - // | UNUSED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | - // | 7 | 6 | 5 4 3 | 2 1 0 | - // | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE - // --------------------------------------------------------------------------------------------------------------------- - // | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 - // | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 - // | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 - // | 1 | 0 | 0 0 0 | 0 0 0 | UNUSED_BIT_MASK 0b10000000 - - // 32bit - // - // | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | - // | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | - // | VR: 0-65535 | VR: 0-65535 | VR = VALUE RANGE - // -------------------------------------------------------------------------------------------------------------------------------- - // | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | DATA_LENGTH_MASK 0xFFFF - // | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | COMMANDID_MASK 0xFFFF0000 - *dst = (byte)encryptionMode; - int offset; - if (responseID == 0) + int offset = 0; + if (chunkLength != length) { - offset = 0; + *dst |= Constants.IS_CHUNKED_1_BIT; + *(int*)(dst + Constants.UDP_HEADER_SIZE) = packetId; + *(int*)(dst + Constants.UDP_HEADER_SIZE + 4) = chunkOffset; + *(int*)(dst + Constants.UDP_HEADER_SIZE + 8) = length; + offset = 12; } - else + + if (responseID != 0) { - offset = 4; - *dst |= RESPONSE_1_BIT; - *(uint*)(dst + 5) = responseID; + *dst |= Constants.RESPONSE_1_BIT; + *(uint*)(dst + Constants.UDP_HEADER_SIZE + offset) = responseID; + offset += 4; } - if (length >= LENGTH_THRESHOLD && compressionMode != CompressionMode.None) + if (length >= Constants.LENGTH_THRESHOLD && compressionMode != CompressionMode.None) { int s; switch (compressionMode) { case CompressionMode.Lz4: - s = LZ4Codec.Encode( - src, length, dst + Constants.UDP_HEADER_SIZE + offset + 4, length); + s = LZ4Codec.Encode(src, length, dst + Constants.UDP_HEADER_SIZE + offset + 4, length); break; default: throw new ArgumentOutOfRangeException( nameof(compressionMode), compressionMode, "Not supported!"); } - if (s > Constants.UDP_PACKET_SIZE_MAX) + if (s > 0 && s < length) { - throw new ArgumentOutOfRangeException( - $"packet size of {Constants.UDP_PACKET_SIZE_MAX} exceeded (s: {s})"); - } - - if (s > 0) - { - size = Constants.UDP_HEADER_SIZE + offset + 4 + s; - *dst |= (byte)compressionMode; *(uint*)(dst + 1) = - ((uint)(s + offset + 4) & DATA_LENGTH_MASK) | (commandID << COMMAND_ID_SHIFT); - *(int*)(dst + offset + 5) = length; - - return; + ((uint)(s + offset + 4) & Constants.DATA_LENGTH_MASK) | + (commandID << Constants.COMMAND_ID_SHIFT); + *(int*)(dst + Constants.UDP_HEADER_SIZE + offset) = length; + return Constants.UDP_HEADER_SIZE + offset + 4 + s; } } - size = Constants.UDP_HEADER_SIZE + offset + length; - *(uint*)(dst + 1) = - ((uint)(length + offset) & DATA_LENGTH_MASK) | (commandID << COMMAND_ID_SHIFT); + ((uint)(length + offset) & Constants.DATA_LENGTH_MASK) | (commandID << Constants.COMMAND_ID_SHIFT); Mem.Cpy(dst + Constants.UDP_HEADER_SIZE + offset, src, length); + return Constants.UDP_HEADER_SIZE + offset + length; } - /// - /// A byte[] extension method that gets header UDP. - /// - /// The header to act on. - /// [out] The packet header. - /// [out] Identifier for the command. - /// [out] Length of the data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void GetHeaderUdp(this byte[] header, - out byte packetHeader, - out uint commandID, - out int dataLength) + internal static bool DeserializeUdp(byte[] buffer, + int bytesTransferred, + BigDataHandler bigDataHandler, + out uint commandID, + out uint responseID, + out byte[] data, + out int dataLength) { - // 8bit - // - // | UNUSED BIT | RESPONSE BIT | COMPRESSED MODE | ENCRYPT MODE | - // | 7 | 6 | 5 4 3 | 2 1 0 | - // | VR: 0/1 | VR: 0/1 | VR: 0-8 | VR: 0-8 | VR = VALUE RANGE - // --------------------------------------------------------------------------------------------------------------------- - // | 0 | 0 | 0 0 0 | 1 1 1 | ENCRYPT_MODE_MASK 0b00000111 - // | 0 | 0 | 1 1 1 | 0 0 0 | COMPRESSED_MODE_MASK 0b00111000 - // | 0 | 1 | 0 0 0 | 0 0 0 | RESPONSE_BIT_MASK 0b01000000 - // | 1 | 0 | 0 0 0 | 0 0 0 | UNUSED_BIT_MASK 0b10000000 - - // 32bit - // - // | COMMANDID 31-16 (16)bit | DATA LENGTH 15-0 (16)bit | - // | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | - // | VR: 0-65535 | VR: 0-65535 | VR = VALUE RANGE - // -------------------------------------------------------------------------------------------------------------------------------- - // | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | DATA_LENGTH_MASK 0xFFFF - // | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | COMMANDID_MASK 0xFFFF0000 - - fixed (byte* ptr = header) + fixed (byte* src = buffer) { - packetHeader = *ptr; - uint h2 = *(uint*)(ptr + 1); - commandID = h2 >> COMMAND_ID_SHIFT; - dataLength = (int)(h2 & DATA_LENGTH_MASK); + byte packetHeader = *src; + uint h2 = *(uint*)(src + 1); + commandID = h2 >> Constants.COMMAND_ID_SHIFT; + dataLength = (int)(h2 & Constants.DATA_LENGTH_MASK); + responseID = 0; + + if (bytesTransferred == dataLength + Constants.UDP_HEADER_SIZE) + { + int offset = 0; + if ((packetHeader & Constants.IS_CHUNKED_1_BIT) != 0) + { + offset += 12; + } + + if ((packetHeader & Constants.RESPONSE_BIT_MASK) != 0) + { + responseID = *(uint*)(src + Constants.UDP_HEADER_SIZE + offset); + offset += 4; + } + + switch ((CompressionMode)(packetHeader & Constants.COMPRESSED_MODE_MASK)) + { + case CompressionMode.Lz4: + int l = *(int*)(src + Constants.UDP_HEADER_SIZE + offset); + offset -= 4; + fixed (byte* dst = data = ByteArrayPool.Rent(l)) + { + int s = LZ4Codec.Decode( + src + Constants.UDP_HEADER_SIZE + offset, dataLength - offset, dst, l); + if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } + + if ((packetHeader & Constants.IS_CHUNKED_1_BIT) != 0) + { + data = bigDataHandler.Receive( + *(int*)(src + Constants.UDP_HEADER_SIZE + offset), dst, l, + *(int*)(src + Constants.UDP_HEADER_SIZE + offset + 4), + *(int*)(src + offset + 8)); + if (data != null) + { + dataLength = data.Length; + return true; + } + return false; + } + } + dataLength = l; + return true; + case CompressionMode.None: + dataLength -= offset; + + if ((packetHeader & Constants.IS_CHUNKED_1_BIT) != 0) + { + data = bigDataHandler.Receive( + *(int*)(src + Constants.UDP_HEADER_SIZE + offset), + src + Constants.UDP_HEADER_SIZE + offset, dataLength, + *(int*)(src + Constants.UDP_HEADER_SIZE + offset + 4), + *(int*)(src + Constants.UDP_HEADER_SIZE + offset + 8)); + if (data != null) + { + dataLength = data.Length; + return true; + } + return false; + } + + fixed (byte* dst = data = ByteArrayPool.Rent(dataLength)) + { + Mem.Cpy(dst, src + Constants.UDP_HEADER_SIZE + offset, dataLength); + } + + return true; + default: + throw new ArgumentOutOfRangeException( + nameof(CompressionMode), + (CompressionMode)(packetHeader & Constants.COMPRESSED_MODE_MASK), + "Not supported!"); + } + } } + data = null; + return false; } } } \ No newline at end of file diff --git a/Exomia.Network/Serialization/Serialization.cs b/Exomia.Network/Serialization/Serialization.cs deleted file mode 100644 index bbc0c3f..0000000 --- a/Exomia.Network/Serialization/Serialization.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region License - -// Copyright (c) 2018-2019, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -namespace Exomia.Network.Serialization -{ - /// - /// A serialization. - /// - static partial class Serialization - { - /// - /// The response bit mask. - /// - internal const uint RESPONSE_BIT_MASK = 0b01000000; - - /// - /// The response bit. - /// - private const byte RESPONSE_1_BIT = 1 << 6; - - /// - /// The compressed mode mask. - /// - internal const byte COMPRESSED_MODE_MASK = 0b00111000; - - /// - /// The command identifier shift. - /// - internal const int COMMAND_ID_SHIFT = 16; - - /// - /// The data length mask. - /// - internal const int DATA_LENGTH_MASK = 0xFFFF; - - /// - /// LENGTH_THRESHOLD 4096. - /// - private const int LENGTH_THRESHOLD = 1 << 12; - - /// - /// The splitted bit. - /// - private const byte SPLIT_1_BIT = 1 << 7; - } -} \ No newline at end of file diff --git a/Exomia.Network/ServerBase.cs b/Exomia.Network/ServerBase.cs index f76134c..1de652e 100644 --- a/Exomia.Network/ServerBase.cs +++ b/Exomia.Network/ServerBase.cs @@ -16,6 +16,7 @@ using Exomia.Network.Buffers; using Exomia.Network.Extensions.Struct; using Exomia.Network.Lib; +using Exomia.Network.Native; using Exomia.Network.Serialization; namespace Exomia.Network @@ -78,6 +79,16 @@ public event ClientCommandDataReceivedHandler ClientDataReceived /// protected readonly Dictionary _clients; + /// + /// Size of the maximum packet. + /// + protected readonly ushort _maxPacketSize; + + /// + /// The big data handler. + /// + private protected readonly BigDataHandler _bigDataHandler; + /// /// The listener. /// @@ -93,6 +104,16 @@ public event ClientCommandDataReceivedHandler ClientDataReceived /// private protected byte _state; + /// + /// The compression mode. + /// + protected CompressionMode _compressionMode = CompressionMode.Lz4; + + /// + /// The encryption mode. + /// + private protected EncryptionMode _encryptionMode = EncryptionMode.None; + /// /// The data received callbacks. /// @@ -118,6 +139,11 @@ public event ClientCommandDataReceivedHandler ClientDataReceived /// private bool _isRunning; + /// + /// Identifier for the packet. + /// + private int _packetID; + /// /// Gets the port. /// @@ -132,15 +158,24 @@ public int Port /// /// Initializes a new instance of the class. /// - private protected ServerBase() + /// Size of the maximum packet. + private protected ServerBase(ushort maxPacketSize) { + _maxPacketSize = maxPacketSize > 0 && maxPacketSize < Constants.UDP_PACKET_SIZE_MAX + ? maxPacketSize + : Constants.UDP_PACKET_SIZE_MAX; + _dataReceivedCallbacks = new Dictionary>(INITIAL_QUEUE_SIZE); _clients = new Dictionary(INITIAL_CLIENT_QUEUE_SIZE); _clientsLock = new SpinLock(Debugger.IsAttached); _dataReceivedCallbacksLock = new SpinLock(Debugger.IsAttached); + _packetID = 1; + _clientDataReceived = new Event>(); + + _bigDataHandler = new BigDataHandler(); } /// @@ -161,14 +196,12 @@ private protected ServerBase() public bool Run(int port) { if (_isRunning) { return true; } - _port = port; - if (OnRun(port, out _listener)) { + _port = port; _state = RECEIVE_FLAG | SEND_FLAG; ListenAsync(); - _isRunning = true; - return true; + return _isRunning = true; } return false; } @@ -363,10 +396,14 @@ private void InvokeClientConnected(T arg0) /// /// The deserialize handler. /// A variable-length parameters list containing command ids. - /// Thrown when one or more required arguments - /// are null. - /// Thrown when one or more arguments are outside - /// the required range. + /// + /// Thrown when one or more required arguments + /// are null. + /// + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// public void AddCommand(DeserializePacketHandler deserialize, params uint[] commandIDs) { if (commandIDs == null) { throw new ArgumentNullException(nameof(commandIDs)); } @@ -405,8 +442,10 @@ public void AddCommand(DeserializePacketHandler deserialize, params uint /// /// True if at least one command is removed, false otherwise. /// - /// Thrown when one or more arguments are outside - /// the required range. + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// public bool RemoveCommands(params uint[] commandIDs) { bool removed = false; @@ -436,12 +475,18 @@ public bool RemoveCommands(params uint[] commandIDs) /// /// Identifier for the command. /// ClientDataReceivedHandler{Socket|Endpoint} - /// Thrown when one or more arguments are outside - /// the required range. - /// Thrown when one or more required arguments - /// are null. - /// Thrown when an exception error condition - /// occurs. + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// + /// + /// Thrown when one or more required arguments + /// are null. + /// + /// + /// Thrown when an exception error condition + /// occurs. + /// public void AddDataReceivedCallback(uint commandID, ClientDataReceivedHandler callback) { if (commandID > Constants.USER_COMMAND_LIMIT) @@ -475,10 +520,14 @@ public void AddDataReceivedCallback(uint commandID, ClientDataReceivedHandler /// Identifier for the command. /// ClientDataReceivedHandler{Socket|Endpoint} - /// Thrown when one or more arguments are outside - /// the required range. - /// Thrown when one or more required arguments - /// are null. + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// + /// + /// Thrown when one or more required arguments + /// are null. + /// public void RemoveDataReceivedCallback(uint commandID, ClientDataReceivedHandler callback) { if (commandID > Constants.USER_COMMAND_LIMIT) @@ -499,12 +548,55 @@ public void RemoveDataReceivedCallback(uint commandID, ClientDataReceivedHandler #region Send - private protected abstract SendError SendTo(T arg0, - uint commandID, - byte[] data, - int offset, - int length, - uint responseID); + private protected abstract unsafe SendError SendTo(T arg0, + int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length); + + private unsafe SendError SendTo(T arg0, + uint commandID, + byte[] data, + int offset, + int length, + uint responseID) + { + if (_listener == null) { return SendError.Invalid; } + if ((_state & SEND_FLAG) == SEND_FLAG) + { + fixed (byte* src = data) + { + if (length > _maxPacketSize) + { + int packetID = Interlocked.Increment(ref _packetID); + int chunkOffset = 0; + int chunkLength = length; + while (chunkLength > _maxPacketSize) + { + SendError se = SendTo( + arg0, packetID, commandID, responseID, + src + offset + chunkOffset, _maxPacketSize, chunkOffset, length); + if (se != SendError.None) + { + return se; + } + chunkLength -= _maxPacketSize; + chunkOffset += _maxPacketSize; + } + return SendTo( + arg0, packetID, commandID, responseID, + src + offset + chunkOffset, _maxPacketSize, chunkOffset, length); + } + return SendTo( + arg0, 0, commandID, responseID, + src + offset, length, 0, length); + } + } + return SendError.Invalid; + } /// public SendError SendTo(TServerClient client, @@ -631,4 +723,105 @@ protected virtual void OnDispose(bool disposing) { } #endregion } + + class BigDataHandler + { + /// + /// The big data buffers. + /// + private readonly Dictionary _bigDataBuffers; + + /// + /// The big data buffer lock. + /// + private SpinLock _bigDataBufferLock; + + /// + /// Initializes a new instance of the class. + /// + public BigDataHandler() + { + _bigDataBufferLock = new SpinLock(Debugger.IsAttached); + _bigDataBuffers = new Dictionary(16); + } + + /// + /// Receives. + /// + /// The key. + /// [in,out] If non-null, source for the. + /// Length of the chunk. + /// The chunk offset. + /// The length. + /// + /// A byte[] or null. + /// + internal unsafe byte[] Receive(int key, + byte* src, + int chunkLength, + int chunkOffset, + int length) + { + if (!_bigDataBuffers.TryGetValue(key, out Buffer bdb)) + { + bool lockTaken = false; + try + { + _bigDataBufferLock.Enter(ref lockTaken); + if (!_bigDataBuffers.TryGetValue(key, out bdb)) + { + _bigDataBuffers.Add( + key, + bdb = new Buffer(new byte[length], length)); + } + } + finally + { + if (lockTaken) { _bigDataBufferLock.Exit(false); } + } + } + + fixed (byte* dst2 = bdb.Data) + { + Mem.Cpy(dst2 + chunkOffset, src, chunkLength); + } + + bdb.BytesLeft -= chunkLength; + if (bdb.BytesLeft == 0) + { + bool lockTaken = false; + try + { + _bigDataBufferLock.Enter(ref lockTaken); + _bigDataBuffers.Remove(key); + } + finally + { + if (lockTaken) { _bigDataBufferLock.Exit(false); } + } + return bdb.Data; + } + return null; + } + + /// + /// Buffer for big data. + /// + private struct Buffer + { + public readonly byte[] Data; + public int BytesLeft; + + /// + /// Initializes a new instance of the struct. + /// + /// The data. + /// The bytes left. + public Buffer(byte[] data, int bytesLeft) + { + Data = data; + BytesLeft = bytesLeft; + } + } + } } \ No newline at end of file diff --git a/Exomia.Network/TCP/TcpClientApm.cs b/Exomia.Network/TCP/TcpClientApm.cs index bfc94f8..f016051 100644 --- a/Exomia.Network/TCP/TcpClientApm.cs +++ b/Exomia.Network/TCP/TcpClientApm.cs @@ -67,50 +67,48 @@ private protected override void ReceiveAsync() } } - private protected override unsafe SendError BeginSendData(uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + private protected override unsafe SendError BeginSendData(int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_clientSocket == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + int size; + byte[] buffer = ByteArrayPool.Rent(Constants.TCP_HEADER_OFFSET + length + 1); + fixed (byte* dst = buffer) { - byte[] send; - int size; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeTcp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, out send, out size); - } + size = Serialization.Serialization.SerializeTcp( + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode); + } - try - { - _clientSocket.BeginSend( - send, 0, size, SocketFlags.None, SendDataCallback, send); - return SendError.None; - } - catch (ObjectDisposedException) - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Aborted); - return SendError.Disposed; - } - catch (SocketException) - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Error); - return SendError.Socket; - } - catch - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Unspecified); - return SendError.Unknown; - } + try + { + _clientSocket.BeginSend( + buffer, 0, size, SocketFlags.None, SendDataCallback, buffer); + return SendError.None; + } + catch (ObjectDisposedException) + { + ByteArrayPool.Return(buffer); + Disconnect(DisconnectReason.Aborted); + return SendError.Disposed; + } + catch (SocketException) + { + ByteArrayPool.Return(buffer); + Disconnect(DisconnectReason.Error); + return SendError.Socket; + } + catch + { + ByteArrayPool.Return(buffer); + Disconnect(DisconnectReason.Unspecified); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -150,7 +148,16 @@ private void ReceiveAsyncCallback(IAsyncResult iar) return; } - Receive(_circularBuffer, _bufferWrite, _bufferRead, bytesTransferred); + if (Serialization.Serialization.DeserializeTcp( + _circularBuffer, _bufferWrite, _bufferRead, bytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + ReceiveAsync(); + DeserializeData(commandID, data, 0, dataLength, responseID); + return; + } + + ReceiveAsync(); } /// diff --git a/Exomia.Network/TCP/TcpClientBase.cs b/Exomia.Network/TCP/TcpClientBase.cs index c64432a..5ac1799 100644 --- a/Exomia.Network/TCP/TcpClientBase.cs +++ b/Exomia.Network/TCP/TcpClientBase.cs @@ -8,12 +8,7 @@ #endregion -using System; using System.Net.Sockets; -using Exomia.Network.Buffers; -using Exomia.Network.Encoding; -using Exomia.Network.Native; -using K4os.Compression.LZ4; namespace Exomia.Network.TCP { @@ -22,21 +17,15 @@ namespace Exomia.Network.TCP /// public abstract class TcpClientBase : ClientBase { - /// - /// Size of the maximum packet. - /// - protected readonly ushort _maxPacketSize; - /// /// Initializes a new instance of the class. /// /// Size of the maximum packet. private protected TcpClientBase(ushort maxPacketSize) - { - _maxPacketSize = maxPacketSize > 0 && maxPacketSize < Constants.TCP_PACKET_SIZE_MAX - ? maxPacketSize - : Constants.TCP_PACKET_SIZE_MAX; - } + : base( + maxPacketSize > 0 && maxPacketSize < Constants.TCP_PACKET_SIZE_MAX + ? maxPacketSize + : Constants.TCP_PACKET_SIZE_MAX) { } /// /// Attempts to create socket. @@ -71,99 +60,5 @@ private protected override bool TryCreateSocket(out Socket socket) return false; } } - - /// - /// Receives. - /// - /// Buffer for circular data. - /// The buffer write. - /// The buffer read. - /// The bytes transferred. - /// - /// Thrown when an exception error condition - /// occurs. - /// - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - private protected unsafe void Receive(CircularBuffer circularBuffer, - byte[] bufferWrite, - byte[] bufferRead, - int bytesTransferred) - { - int size = circularBuffer.Write(bufferWrite, 0, bytesTransferred); - while (circularBuffer.PeekHeader( - 0, out byte packetHeader, out uint commandID, out int dataLength, out ushort checksum) - && dataLength <= circularBuffer.Count - Constants.TCP_HEADER_SIZE) - { - if (circularBuffer.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b) && - b == Constants.ZERO_BYTE) - { - fixed (byte* ptr = bufferRead) - { - circularBuffer.Read(ptr, 0, dataLength, Constants.TCP_HEADER_SIZE); - if (size < bytesTransferred) - { - circularBuffer.Write(bufferWrite, size, bytesTransferred - size); - } - - uint responseID = 0; - int offset = 0; - if ((packetHeader & Serialization.Serialization.RESPONSE_BIT_MASK) != 0) - { - responseID = *(uint*)ptr; - offset = 4; - } - - CompressionMode compressionMode = - (CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK); - if (compressionMode != CompressionMode.None) - { - offset += 4; - } - - byte[] deserializeBuffer = ByteArrayPool.Rent(dataLength); - if (PayloadEncoding.Decode( - ptr, offset, dataLength - 1, deserializeBuffer, out int bufferLength) == checksum) - { - switch (compressionMode) - { - case CompressionMode.None: - break; - case CompressionMode.Lz4: - int l = *(int*)(ptr + offset); - - byte[] buffer = ByteArrayPool.Rent(l); - int s = LZ4Codec.Decode(deserializeBuffer, 0, bufferLength, buffer, 0, l); - if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } - - ByteArrayPool.Return(deserializeBuffer); - deserializeBuffer = buffer; - bufferLength = l; - break; - default: - throw new ArgumentOutOfRangeException( - nameof(CompressionMode), - (CompressionMode)(packetHeader & - Serialization.Serialization.COMPRESSED_MODE_MASK), - "Not supported!"); - } - ReceiveAsync(); - DeserializeData(commandID, deserializeBuffer, 0, bufferLength, responseID); - return; - } - break; - } - } - bool skipped = circularBuffer.SkipUntil(Constants.TCP_HEADER_SIZE, Constants.ZERO_BYTE); - if (size < bytesTransferred) - { - size += circularBuffer.Write(bufferWrite, size, bytesTransferred - size); - } - if (!skipped && !circularBuffer.SkipUntil(0, Constants.ZERO_BYTE)) { break; } - } - ReceiveAsync(); - } } } \ No newline at end of file diff --git a/Exomia.Network/TCP/TcpClientEap.cs b/Exomia.Network/TCP/TcpClientEap.cs index d1b0559..0ab60df 100644 --- a/Exomia.Network/TCP/TcpClientEap.cs +++ b/Exomia.Network/TCP/TcpClientEap.cs @@ -76,57 +76,55 @@ private protected override void ReceiveAsync() } } - private protected override unsafe SendError BeginSendData(uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + private protected override unsafe SendError BeginSendData(int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_clientSocket == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); + if (sendEventArgs == null) { - SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); - if (sendEventArgs == null) - { - sendEventArgs = new SocketAsyncEventArgs(); - sendEventArgs.Completed += SendAsyncCompleted; - sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); - } + sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += SendAsyncCompleted; + sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); + } - fixed (byte* src = data) - fixed (byte* dst = sendEventArgs.Buffer) - { + fixed (byte* dst = sendEventArgs.Buffer) + { + sendEventArgs.SetBuffer( + 0, Serialization.Serialization.SerializeTcp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, dst, out int size); - sendEventArgs.SetBuffer(0, size); - } + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode)); + } - try - { - if (!_clientSocket.SendAsync(sendEventArgs)) - { - SendAsyncCompleted(_clientSocket, sendEventArgs); - } - return SendError.None; - } - catch (ObjectDisposedException) - { - Disconnect(DisconnectReason.Aborted); - return SendError.Disposed; - } - catch (SocketException) - { - Disconnect(DisconnectReason.Error); - return SendError.Socket; - } - catch + try + { + if (!_clientSocket.SendAsync(sendEventArgs)) { - Disconnect(DisconnectReason.Unspecified); - return SendError.Unknown; + SendAsyncCompleted(_clientSocket, sendEventArgs); } + return SendError.None; + } + catch (ObjectDisposedException) + { + Disconnect(DisconnectReason.Aborted); + return SendError.Disposed; + } + catch (SocketException) + { + Disconnect(DisconnectReason.Error); + return SendError.Socket; + } + catch + { + Disconnect(DisconnectReason.Unspecified); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -155,7 +153,15 @@ private void ReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) return; } - Receive(_circularBuffer, e.Buffer, _bufferRead, bytesTransferred); + if (Serialization.Serialization.DeserializeTcp( + _circularBuffer, e.Buffer, _bufferRead, bytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + ReceiveAsync(); + DeserializeData(commandID, data, 0, dataLength, responseID); + return; + } + ReceiveAsync(); } /// diff --git a/Exomia.Network/TCP/TcpServerApmBase.cs b/Exomia.Network/TCP/TcpServerApmBase.cs index e08ea34..e65d6e7 100644 --- a/Exomia.Network/TCP/TcpServerApmBase.cs +++ b/Exomia.Network/TCP/TcpServerApmBase.cs @@ -30,50 +30,49 @@ protected TcpServerApmBase(ushort maxPacketSize = Constants.TCP_PACKET_SIZE_MAX) : base(maxPacketSize) { } private protected override unsafe SendError SendTo(Socket arg0, + int packetID, uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_listener == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + SendStateObject state; + state.Buffer = ByteArrayPool.Rent(Constants.TCP_HEADER_OFFSET + length + 1); + state.Socket = arg0; + int size; + fixed (byte* dst = state.Buffer) { - SendStateObject state; - state.Socket = arg0; - int size; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeTcp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, out state.Buffer, out size); - } + size = Serialization.Serialization.SerializeTcp( + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode); + } - try - { - arg0.BeginSend(state.Buffer, 0, size, SocketFlags.None, BeginSendCallback, state); - return SendError.None; - } - catch (ObjectDisposedException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Aborted); - ByteArrayPool.Return(state.Buffer); - return SendError.Disposed; - } - catch (SocketException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Error); - ByteArrayPool.Return(state.Buffer); - return SendError.Socket; - } - catch - { - InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); - ByteArrayPool.Return(state.Buffer); - return SendError.Unknown; - } + try + { + arg0.BeginSend(state.Buffer, 0, size, SocketFlags.None, BeginSendCallback, state); + return SendError.None; + } + catch (ObjectDisposedException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Aborted); + ByteArrayPool.Return(state.Buffer); + return SendError.Disposed; + } + catch (SocketException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Error); + ByteArrayPool.Return(state.Buffer); + return SendError.Socket; + } + catch + { + InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); + ByteArrayPool.Return(state.Buffer); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -201,7 +200,15 @@ private void ReceiveDataCallback(IAsyncResult iar) return; } - Receive(state.Socket, state.CircularBuffer, state.BufferWrite, state.BufferRead, bytesTransferred); + if (Serialization.Serialization.DeserializeTcp( + state.CircularBuffer, state.BufferWrite, state.BufferRead, bytesTransferred, + _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + ReceiveAsync(state); + DeserializeData(state.Socket, commandID, data, 0, dataLength, responseID); + return; + } ReceiveAsync(state); } diff --git a/Exomia.Network/TCP/TcpServerBase.cs b/Exomia.Network/TCP/TcpServerBase.cs index e363838..0342112 100644 --- a/Exomia.Network/TCP/TcpServerBase.cs +++ b/Exomia.Network/TCP/TcpServerBase.cs @@ -8,13 +8,8 @@ #endregion -using System; using System.Net; using System.Net.Sockets; -using Exomia.Network.Buffers; -using Exomia.Network.Encoding; -using Exomia.Network.Native; -using K4os.Compression.LZ4; namespace Exomia.Network.TCP { @@ -25,21 +20,15 @@ namespace Exomia.Network.TCP public abstract class TcpServerBase : ServerBase where TServerClient : ServerClientBase { - /// - /// Size of the maximum packet. - /// - protected readonly ushort _maxPacketSize; - /// /// Initializes a new instance of the class. /// /// Size of the maximum packet. private protected TcpServerBase(ushort maxPacketSize) - { - _maxPacketSize = maxPacketSize > 0 && maxPacketSize < Constants.TCP_PACKET_SIZE_MAX - ? maxPacketSize - : Constants.TCP_PACKET_SIZE_MAX; - } + : base( + maxPacketSize > 0 && maxPacketSize < Constants.TCP_PACKET_SIZE_MAX + ? maxPacketSize + : Constants.TCP_PACKET_SIZE_MAX) { } /// private protected override bool OnRun(int port, out Socket listener) @@ -85,98 +74,5 @@ private protected override void OnAfterClientDisconnect(TServerClient client) /* IGNORE */ } } - - /// - /// Receives. - /// - /// The socket. - /// Buffer for circular data. - /// The buffer write. - /// The buffer read. - /// The bytes transferred. - /// - /// Thrown when an exception error condition - /// occurs. - /// - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - private protected unsafe void Receive(Socket socket, - CircularBuffer circularBuffer, - byte[] bufferWrite, - byte[] bufferRead, - int bytesTransferred) - { - int size = circularBuffer.Write(bufferWrite, 0, bytesTransferred); - while (circularBuffer.PeekHeader( - 0, out byte packetHeader, out uint commandID, out int dataLength, out ushort checksum) - && dataLength <= circularBuffer.Count - Constants.TCP_HEADER_SIZE) - { - if (circularBuffer.PeekByte((Constants.TCP_HEADER_SIZE + dataLength) - 1, out byte b) && - b == Constants.ZERO_BYTE) - { - fixed (byte* ptr = bufferRead) - { - circularBuffer.Read(ptr, 0, dataLength, Constants.TCP_HEADER_SIZE); - if (size < bytesTransferred) - { - circularBuffer.Write(bufferWrite, size, bytesTransferred - size); - } - - uint responseID = 0; - int offset = 0; - if ((packetHeader & Serialization.Serialization.RESPONSE_BIT_MASK) != 0) - { - responseID = *(uint*)ptr; - offset = 4; - } - - CompressionMode compressionMode = - (CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK); - if (compressionMode != CompressionMode.None) - { - offset += 4; - } - - byte[] deserializeBuffer = ByteArrayPool.Rent(dataLength); - if (PayloadEncoding.Decode( - ptr, offset, dataLength - 1, deserializeBuffer, out int bufferLength) == checksum) - { - switch (compressionMode) - { - case CompressionMode.None: - break; - case CompressionMode.Lz4: - int l = *(int*)(ptr + offset); - - byte[] buffer = ByteArrayPool.Rent(l); - int s = LZ4Codec.Decode(deserializeBuffer, 0, bufferLength, buffer, 0, l); - if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } - - ByteArrayPool.Return(deserializeBuffer); - deserializeBuffer = buffer; - bufferLength = l; - break; - default: - throw new ArgumentOutOfRangeException( - nameof(CompressionMode), - (CompressionMode)(packetHeader & - Serialization.Serialization.COMPRESSED_MODE_MASK), - "Not supported!"); - } - DeserializeData(socket, commandID, deserializeBuffer, 0, bufferLength, responseID); - } - return; - } - } - bool skipped = circularBuffer.SkipUntil(Constants.TCP_HEADER_SIZE, Constants.ZERO_BYTE); - if (size < bytesTransferred) - { - size += circularBuffer.Write(bufferWrite, size, bytesTransferred - size); - } - if (!skipped && !circularBuffer.SkipUntil(0, Constants.ZERO_BYTE)) { return; } - } - } } } \ No newline at end of file diff --git a/Exomia.Network/TCP/TcpServerEapBase.cs b/Exomia.Network/TCP/TcpServerEapBase.cs index 8b6ae39..11c512d 100644 --- a/Exomia.Network/TCP/TcpServerEapBase.cs +++ b/Exomia.Network/TCP/TcpServerEapBase.cs @@ -39,62 +39,60 @@ protected TcpServerEapBase(ushort expectedMaxClients = 32, ushort maxPacketSize } private protected override unsafe SendError SendTo(Socket arg0, + int packetID, uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_listener == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) - { - SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); + SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); - if (sendEventArgs == null) - { - sendEventArgs = new SocketAsyncEventArgs(); - sendEventArgs.Completed += SendAsyncCompleted; - sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); - } + if (sendEventArgs == null) + { + sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += SendAsyncCompleted; + sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); + } + sendEventArgs.AcceptSocket = arg0; - fixed (byte* src = data) - fixed (byte* dst = sendEventArgs.Buffer) - { + fixed (byte* dst = sendEventArgs.Buffer) + { + sendEventArgs.SetBuffer( + 0, Serialization.Serialization.SerializeTcp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, dst, out int size); - sendEventArgs.SetBuffer(0, size); - } - sendEventArgs.AcceptSocket = arg0; + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode)); + } - try - { - if (!arg0.SendAsync(sendEventArgs)) - { - SendAsyncCompleted(arg0, sendEventArgs); - } - return SendError.None; - } - catch (ObjectDisposedException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Aborted); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Disposed; - } - catch (SocketException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Error); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Socket; - } - catch + try + { + if (!arg0.SendAsync(sendEventArgs)) { - InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Unknown; + SendAsyncCompleted(arg0, sendEventArgs); } + return SendError.None; + } + catch (ObjectDisposedException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Aborted); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Disposed; + } + catch (SocketException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Error); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Socket; + } + catch + { + InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -205,7 +203,14 @@ private void ReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) ServerClientStateObject state = (ServerClientStateObject)e.UserToken; - Receive(e.AcceptSocket, state.CircularBuffer, e.Buffer, state.BufferRead, bytesTransferred); + if (Serialization.Serialization.DeserializeTcp( + state.CircularBuffer, e.Buffer, state.BufferRead, bytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + ReceiveAsync(e); + DeserializeData(e.AcceptSocket, commandID, data, 0, dataLength, responseID); + return; + } ReceiveAsync(e); } diff --git a/Exomia.Network/UDP/UdpClientApm.cs b/Exomia.Network/UDP/UdpClientApm.cs index c710d0b..19f7704 100644 --- a/Exomia.Network/UDP/UdpClientApm.cs +++ b/Exomia.Network/UDP/UdpClientApm.cs @@ -63,50 +63,49 @@ private protected override void ReceiveAsync() } } - private protected override unsafe SendError BeginSendData(uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + /// + private protected override unsafe SendError BeginSendData(int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_clientSocket == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + int size; + byte[] buffer = ByteArrayPool.Rent(Constants.UDP_HEADER_OFFSET + length); + fixed (byte* dst = buffer) { - byte[] send; - int size; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeUdp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, out send, out size); - } + size = Serialization.Serialization.SerializeUdp( + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode); + } - try - { - _clientSocket.BeginSend( - send, 0, size, SocketFlags.None, SendDataCallback, send); - return SendError.None; - } - catch (ObjectDisposedException) - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Aborted); - return SendError.Disposed; - } - catch (SocketException) - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Error); - return SendError.Socket; - } - catch - { - ByteArrayPool.Return(send); - Disconnect(DisconnectReason.Unspecified); - return SendError.Unknown; - } + try + { + _clientSocket.BeginSend( + buffer, 0, size, SocketFlags.None, SendDataCallback, buffer); + return SendError.None; + } + catch (ObjectDisposedException) + { + Disconnect(DisconnectReason.Aborted); + ByteArrayPool.Return(buffer); + return SendError.Disposed; + } + catch (SocketException) + { + Disconnect(DisconnectReason.Error); + ByteArrayPool.Return(buffer); + return SendError.Socket; + } + catch + { + Disconnect(DisconnectReason.Unspecified); + ByteArrayPool.Return(buffer); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -141,8 +140,15 @@ private void ReceiveAsyncCallback(IAsyncResult iar) return; } + ReceiveAsync(); + ClientStateObject state = (ClientStateObject)iar.AsyncState; - Receive(state.Buffer, bytesTransferred); + if (Serialization.Serialization.DeserializeUdp( + state.Buffer, bytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + DeserializeData(commandID, data, 0, dataLength, responseID); + } _clientStateObjectPool.Return(state); } diff --git a/Exomia.Network/UDP/UdpClientBase.cs b/Exomia.Network/UDP/UdpClientBase.cs index bcff6dc..ad9c833 100644 --- a/Exomia.Network/UDP/UdpClientBase.cs +++ b/Exomia.Network/UDP/UdpClientBase.cs @@ -8,12 +8,7 @@ #endregion -using System; using System.Net.Sockets; -using Exomia.Network.Buffers; -using Exomia.Network.Native; -using Exomia.Network.Serialization; -using K4os.Compression.LZ4; namespace Exomia.Network.UDP { @@ -22,21 +17,15 @@ namespace Exomia.Network.UDP /// public abstract class UdpClientBase : ClientBase { - /// - /// Size of the maximum packet. - /// - protected readonly ushort _maxPacketSize; - /// /// Initializes a new instance of the class. /// /// Size of the maximum packet. private protected UdpClientBase(ushort maxPacketSize) - { - _maxPacketSize = maxPacketSize > 0 && maxPacketSize < Constants.UDP_PACKET_SIZE_MAX - ? maxPacketSize - : Constants.UDP_PACKET_SIZE_MAX; - } + : base( + maxPacketSize > 0 && maxPacketSize < Constants.UDP_PACKET_SIZE_MAX + ? maxPacketSize + : Constants.UDP_PACKET_SIZE_MAX) { } /// private protected override bool TryCreateSocket(out Socket socket) @@ -65,58 +54,5 @@ private protected override bool TryCreateSocket(out Socket socket) return false; } } - - private protected unsafe void Receive(byte[] buffer, int bytesTransferred) - { - ReceiveAsync(); - - buffer.GetHeaderUdp(out byte packetHeader, out uint commandID, out int dataLength); - - if (bytesTransferred == dataLength + Constants.UDP_HEADER_SIZE) - { - uint responseID = 0; - int offset = 0; - fixed (byte* src = buffer) - { - if ((packetHeader & Serialization.Serialization.RESPONSE_BIT_MASK) != 0) - { - responseID = *(uint*)src; - offset = 4; - } - byte[] payload; - switch ((CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK)) - { - case CompressionMode.Lz4: - int l = *(int*)(src + offset); - offset += 4; - - payload = ByteArrayPool.Rent(l); - fixed (byte* dst = payload) - { - int s = LZ4Codec.Decode( - src + Constants.UDP_HEADER_SIZE + offset, dataLength - offset, dst, l); - if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } - } - DeserializeData(commandID, payload, 0, l, responseID); - break; - case CompressionMode.None: - dataLength -= offset; - payload = ByteArrayPool.Rent(dataLength); - - fixed (byte* dest = payload) - { - Mem.Cpy(dest, src + Constants.UDP_HEADER_SIZE + offset, dataLength); - } - DeserializeData(commandID, payload, 0, dataLength, responseID); - break; - default: - throw new ArgumentOutOfRangeException( - nameof(CompressionMode), - (CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK), - "Not supported!"); - } - } - } - } } } \ No newline at end of file diff --git a/Exomia.Network/UDP/UdpClientEap.cs b/Exomia.Network/UDP/UdpClientEap.cs index 5abd2e6..d27b118 100644 --- a/Exomia.Network/UDP/UdpClientEap.cs +++ b/Exomia.Network/UDP/UdpClientEap.cs @@ -78,60 +78,59 @@ private protected override void ReceiveAsync() } } - private protected override unsafe SendError BeginSendData(uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + /// + private protected override unsafe SendError BeginSendData(int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_clientSocket == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); + if (sendEventArgs == null) { - SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); - if (sendEventArgs == null) - { - sendEventArgs = new SocketAsyncEventArgs(); - sendEventArgs.Completed += SendAsyncCompleted; - sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); - } + sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += SendAsyncCompleted; + sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); + } - fixed (byte* src = data) - fixed (byte* dst = sendEventArgs.Buffer) - { + fixed (byte* dst = sendEventArgs.Buffer) + { + sendEventArgs.SetBuffer( + 0, Serialization.Serialization.SerializeUdp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, dst, out int size); - sendEventArgs.SetBuffer(0, size); - } + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode)); + } - try - { - if (!_clientSocket.SendAsync(sendEventArgs)) - { - SendAsyncCompleted(_clientSocket, sendEventArgs); - } - return SendError.None; - } - catch (ObjectDisposedException) - { - Disconnect(DisconnectReason.Aborted); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Disposed; - } - catch (SocketException) - { - Disconnect(DisconnectReason.Error); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Socket; - } - catch + try + { + if (!_clientSocket.SendAsync(sendEventArgs)) { - Disconnect(DisconnectReason.Unspecified); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Unknown; + SendAsyncCompleted(sendEventArgs.RemoteEndPoint, sendEventArgs); } + return SendError.None; + } + catch (ObjectDisposedException) + { + Disconnect(DisconnectReason.Aborted); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Disposed; + } + catch (SocketException) + { + Disconnect(DisconnectReason.Error); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Socket; + } + catch + { + Disconnect(DisconnectReason.Unspecified); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -153,8 +152,14 @@ private void ReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) return; } - Receive(e.Buffer, e.BytesTransferred); - _receiveEventArgsPool.Return(e); + ReceiveAsync(); + + if (Serialization.Serialization.DeserializeUdp( + e.Buffer, e.BytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + DeserializeData(commandID, data, 0, dataLength, responseID); + } } /// diff --git a/Exomia.Network/UDP/UdpServerApmBase.cs b/Exomia.Network/UDP/UdpServerApmBase.cs index ffdaad1..c07af4a 100644 --- a/Exomia.Network/UDP/UdpServerApmBase.cs +++ b/Exomia.Network/UDP/UdpServerApmBase.cs @@ -39,49 +39,47 @@ protected UdpServerApmBase(ushort expectedMaxClients, ushort maxPacketSize = Con } private protected override unsafe SendError SendTo(EndPoint arg0, + int packetID, uint commandID, - byte[] data, - int offset, - int length, - uint responseID) + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) { - if (_listener == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) + int size; + byte[] buffer = ByteArrayPool.Rent(Constants.UDP_HEADER_OFFSET + length); + fixed (byte* dst = buffer) { - byte[] send; - int size; - fixed (byte* src = data) - { - Serialization.Serialization.SerializeUdp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, out send, out size); - } + size = Serialization.Serialization.SerializeUdp( + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode); + } - try - { - _listener.BeginSendTo(send, 0, size, SocketFlags.None, arg0, SendDataToCallback, send); - return SendError.None; - } - catch (ObjectDisposedException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Aborted); - ByteArrayPool.Return(send); - return SendError.Disposed; - } - catch (SocketException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Error); - ByteArrayPool.Return(send); - return SendError.Socket; - } - catch - { - InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); - ByteArrayPool.Return(send); - return SendError.Unknown; - } + try + { + _listener.BeginSendTo(buffer, 0, size, SocketFlags.None, arg0, SendDataToCallback, buffer); + return SendError.None; + } + catch (ObjectDisposedException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Aborted); + ByteArrayPool.Return(buffer); + return SendError.Disposed; + } + catch (SocketException) + { + InvokeClientDisconnect(arg0, DisconnectReason.Error); + ByteArrayPool.Return(buffer); + return SendError.Socket; + } + catch + { + InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); + ByteArrayPool.Return(buffer); + return SendError.Unknown; } - return SendError.Invalid; } /// @@ -164,7 +162,12 @@ private void ReceiveDataCallback(IAsyncResult iar) ListenAsync(); - Receive(state.Buffer, bytesTransferred, state.EndPoint); + if (Serialization.Serialization.DeserializeUdp( + state.Buffer, bytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + DeserializeData(state.EndPoint, commandID, data, 0, dataLength, responseID); + } _serverClientStateObjectPool.Return(state); } diff --git a/Exomia.Network/UDP/UdpServerBase.cs b/Exomia.Network/UDP/UdpServerBase.cs index 026604c..affd6e2 100644 --- a/Exomia.Network/UDP/UdpServerBase.cs +++ b/Exomia.Network/UDP/UdpServerBase.cs @@ -8,13 +8,8 @@ #endregion -using System; using System.Net; using System.Net.Sockets; -using Exomia.Network.Buffers; -using Exomia.Network.Native; -using Exomia.Network.Serialization; -using K4os.Compression.LZ4; namespace Exomia.Network.UDP { @@ -25,21 +20,15 @@ namespace Exomia.Network.UDP public abstract class UdpServerBase : ServerBase where TServerClient : ServerClientBase { - /// - /// Size of the maximum packet. - /// - protected readonly ushort _maxPacketSize; - /// /// Initializes a new instance of the class. /// /// Size of the maximum packet. protected UdpServerBase(ushort maxPacketSize) - { - _maxPacketSize = maxPacketSize > 0 && maxPacketSize < Constants.UDP_PACKET_SIZE_MAX - ? maxPacketSize - : Constants.UDP_PACKET_SIZE_MAX; - } + : base( + maxPacketSize > 0 && maxPacketSize < Constants.UDP_PACKET_SIZE_MAX + ? maxPacketSize + : Constants.UDP_PACKET_SIZE_MAX) { } /// /// Executes the run action. @@ -77,69 +66,5 @@ private protected override bool OnRun(int port, out Socket listener) return false; } } - - /// - /// Receives. - /// - /// The buffer. - /// The bytes transferred. - /// The ep. - /// - /// Thrown when an exception error condition - /// occurs. - /// - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - private protected unsafe void Receive(byte[] buffer, int bytesTransferred, EndPoint ep) - { - buffer.GetHeaderUdp(out byte packetHeader, out uint commandID, out int dataLength); - - if (bytesTransferred == dataLength + Constants.UDP_HEADER_SIZE) - { - uint responseID = 0; - int offset = 0; - fixed (byte* src = buffer) - { - if ((packetHeader & Serialization.Serialization.RESPONSE_BIT_MASK) != 0) - { - responseID = *(uint*)src; - offset = 4; - } - byte[] payload; - switch ((CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK)) - { - case CompressionMode.Lz4: - int l = *(int*)(src + offset); - offset += 4; - - payload = ByteArrayPool.Rent(l); - int s = LZ4Codec.Decode( - buffer, Constants.UDP_HEADER_SIZE + offset, dataLength - offset, payload, 0, l); - if (s != l) { throw new Exception("LZ4.Decode FAILED!"); } - - DeserializeData(ep, commandID, payload, 0, l, responseID); - break; - case CompressionMode.None: - dataLength -= offset; - payload = ByteArrayPool.Rent(dataLength); - - fixed (byte* dest = payload) - { - Mem.Cpy(dest, src + Constants.UDP_HEADER_SIZE + offset, dataLength); - } - - DeserializeData(ep, commandID, payload, 0, dataLength, responseID); - break; - default: - throw new ArgumentOutOfRangeException( - nameof(CompressionMode), - (CompressionMode)(packetHeader & Serialization.Serialization.COMPRESSED_MODE_MASK), - "Not supported!"); - } - } - } - } } } \ No newline at end of file diff --git a/Exomia.Network/UDP/UdpServerEapBase.cs b/Exomia.Network/UDP/UdpServerEapBase.cs index 003f983..2344558 100644 --- a/Exomia.Network/UDP/UdpServerEapBase.cs +++ b/Exomia.Network/UDP/UdpServerEapBase.cs @@ -43,65 +43,6 @@ protected UdpServerEapBase(ushort expectedMaxClients, ushort maxPacketSize = Con _sendEventArgsPool = new SocketAsyncEventArgsPool((ushort)(expectedMaxClients + 5)); } - private protected override unsafe SendError SendTo(EndPoint arg0, - uint commandID, - byte[] data, - int offset, - int length, - uint responseID) - { - if (_listener == null) { return SendError.Invalid; } - if ((_state & SEND_FLAG) == SEND_FLAG) - { - SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); - if (sendEventArgs == null) - { - sendEventArgs = new SocketAsyncEventArgs(); - sendEventArgs.Completed += SendToAsyncCompleted; - sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); - } - - fixed (byte* src = data) - fixed (byte* dst = sendEventArgs.Buffer) - { - Serialization.Serialization.SerializeUdp( - commandID, src + offset, length, responseID, EncryptionMode.None, - CompressionMode.Lz4, dst, out int size); - sendEventArgs.SetBuffer(0, size); - } - - sendEventArgs.RemoteEndPoint = arg0; - - try - { - if (!_listener.SendToAsync(sendEventArgs)) - { - SendToAsyncCompleted(arg0, sendEventArgs); - } - return SendError.None; - } - catch (ObjectDisposedException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Aborted); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Disposed; - } - catch (SocketException) - { - InvokeClientDisconnect(arg0, DisconnectReason.Error); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Socket; - } - catch - { - InvokeClientDisconnect(arg0, DisconnectReason.Unspecified); - _sendEventArgsPool.Return(sendEventArgs); - return SendError.Unknown; - } - } - return SendError.Invalid; - } - /// /// Listen asynchronous. /// @@ -143,6 +84,63 @@ protected override void OnDispose(bool disposing) } } + /// + private protected override unsafe SendError SendTo(EndPoint arg0, + int packetID, + uint commandID, + uint responseID, + byte* src, + int chunkLength, + int chunkOffset, + int length) + { + SocketAsyncEventArgs sendEventArgs = _sendEventArgsPool.Rent(); + if (sendEventArgs == null) + { + sendEventArgs = new SocketAsyncEventArgs(); + sendEventArgs.Completed += SendToAsyncCompleted; + sendEventArgs.SetBuffer(new byte[_maxPacketSize], 0, _maxPacketSize); + } + sendEventArgs.RemoteEndPoint = arg0; + + fixed (byte* dst = sendEventArgs.Buffer) + { + sendEventArgs.SetBuffer( + 0, + Serialization.Serialization.SerializeUdp( + packetID, commandID, responseID, + src, dst, chunkLength, chunkOffset, length, + _encryptionMode, _compressionMode)); + } + + try + { + if (!_listener.SendToAsync(sendEventArgs)) + { + SendToAsyncCompleted(sendEventArgs.RemoteEndPoint, sendEventArgs); + } + return SendError.None; + } + catch (ObjectDisposedException) + { + InvokeClientDisconnect(sendEventArgs.RemoteEndPoint, DisconnectReason.Aborted); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Disposed; + } + catch (SocketException) + { + InvokeClientDisconnect(sendEventArgs.RemoteEndPoint, DisconnectReason.Error); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Socket; + } + catch + { + InvokeClientDisconnect(sendEventArgs.RemoteEndPoint, DisconnectReason.Unspecified); + _sendEventArgsPool.Return(sendEventArgs); + return SendError.Unknown; + } + } + /// /// Receive from asynchronous completed. /// @@ -164,7 +162,12 @@ private void ReceiveFromAsyncCompleted(object sender, SocketAsyncEventArgs e) ListenAsync(); - Receive(e.Buffer, e.BytesTransferred, e.RemoteEndPoint); + if (Serialization.Serialization.DeserializeUdp( + e.Buffer, e.BytesTransferred, _bigDataHandler, + out uint commandID, out uint responseID, out byte[] data, out int dataLength)) + { + DeserializeData(e.RemoteEndPoint, commandID, data, 0, dataLength, responseID); + } _receiveEventArgsPool.Return(e); } diff --git a/README.md b/README.md index 54ab1b0..263dbb1 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ static async Task Main(string[] args) if (res) { Console.WriteLine(i + - "ping received " + TimeSpan.FromTicks((DateTime.Now.Ticks - res.Result.TimeStamp) / 2) + "ping received " + TimeSpan.FromTicks((DateTime.Now.Ticks - res.Result.Timestamp) / 2) .TotalMilliseconds); } else { Console.WriteLine("error receiving response"); } @@ -120,7 +120,7 @@ static async Task Main(string[] args) if (res) { Console.WriteLine(i + - "ping received " + TimeSpan.FromTicks((DateTime.Now.Ticks - res.Result.TimeStamp) / 2) + "ping received " + TimeSpan.FromTicks((DateTime.Now.Ticks - res.Result.Timestamp) / 2) .TotalMilliseconds); } else { Console.WriteLine("error receiving response"); } From 58dabaff3ab0fda08001a792e6cfbfa58ace4fe1 Mon Sep 17 00:00:00 2001 From: Daniel Baetz Date: Fri, 2 Aug 2019 00:01:28 +0200 Subject: [PATCH 3/4] updated wiki --- network.wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network.wiki b/network.wiki index 334f7d7..5b88491 160000 --- a/network.wiki +++ b/network.wiki @@ -1 +1 @@ -Subproject commit 334f7d7fe7ca5f77975b7ecf08e445b241537b3b +Subproject commit 5b884911bc6ce78d684c72fecda2dfc7c0ecfcc3 From 29c38271b6cc3e769ccb534c5708bde84de33bd9 Mon Sep 17 00:00:00 2001 From: Daniel Baetz Date: Fri, 2 Aug 2019 00:02:35 +0200 Subject: [PATCH 4/4] updated wiki --- Exomia.Network/Encoding/PayloadEncoding.cs | 1 - Exomia.Network/ServerBase.cs | 103 ++++++++++++--------- network.wiki | 2 +- 3 files changed, 62 insertions(+), 44 deletions(-) diff --git a/Exomia.Network/Encoding/PayloadEncoding.cs b/Exomia.Network/Encoding/PayloadEncoding.cs index 189901a..d9909a7 100644 --- a/Exomia.Network/Encoding/PayloadEncoding.cs +++ b/Exomia.Network/Encoding/PayloadEncoding.cs @@ -141,7 +141,6 @@ private static void Encode(uint* checksum, byte* buffer, byte* data, int size) /// [in,out] If non-null, the checksum. /// [in,out] If non-null, destination for the. /// [in,out] If non-null, source for the. - /// The second int. /// The size. private static void Decode(uint* checksum, byte* dest, byte* src, int size) { diff --git a/Exomia.Network/ServerBase.cs b/Exomia.Network/ServerBase.cs index 1de652e..8734e70 100644 --- a/Exomia.Network/ServerBase.cs +++ b/Exomia.Network/ServerBase.cs @@ -396,14 +396,10 @@ private void InvokeClientConnected(T arg0) /// /// The deserialize handler. /// A variable-length parameters list containing command ids. - /// - /// Thrown when one or more required arguments - /// are null. - /// - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// + /// Thrown when one or more required arguments + /// are null. + /// Thrown when one or more arguments are outside + /// the required range. public void AddCommand(DeserializePacketHandler deserialize, params uint[] commandIDs) { if (commandIDs == null) { throw new ArgumentNullException(nameof(commandIDs)); } @@ -442,10 +438,8 @@ public void AddCommand(DeserializePacketHandler deserialize, params uint /// /// True if at least one command is removed, false otherwise. /// - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// + /// Thrown when one or more arguments are outside + /// the required range. public bool RemoveCommands(params uint[] commandIDs) { bool removed = false; @@ -475,18 +469,12 @@ public bool RemoveCommands(params uint[] commandIDs) /// /// Identifier for the command. /// ClientDataReceivedHandler{Socket|Endpoint} - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - /// - /// Thrown when one or more required arguments - /// are null. - /// - /// - /// Thrown when an exception error condition - /// occurs. - /// + /// Thrown when one or more arguments are outside + /// the required range. + /// Thrown when one or more required arguments + /// are null. + /// Thrown when an exception error condition + /// occurs. public void AddDataReceivedCallback(uint commandID, ClientDataReceivedHandler callback) { if (commandID > Constants.USER_COMMAND_LIMIT) @@ -520,14 +508,10 @@ public void AddDataReceivedCallback(uint commandID, ClientDataReceivedHandler /// Identifier for the command. /// ClientDataReceivedHandler{Socket|Endpoint} - /// - /// Thrown when one or more arguments are outside - /// the required range. - /// - /// - /// Thrown when one or more required arguments - /// are null. - /// + /// Thrown when one or more arguments are outside + /// the required range. + /// Thrown when one or more required arguments + /// are null. public void RemoveDataReceivedCallback(uint commandID, ClientDataReceivedHandler callback) { if (commandID > Constants.USER_COMMAND_LIMIT) @@ -548,6 +532,20 @@ public void RemoveDataReceivedCallback(uint commandID, ClientDataReceivedHandler #region Send + /// + /// Sends to. + /// + /// Socket|Endpoint. + /// Identifier for the packet. + /// Identifier for the command. + /// Identifier for the response. + /// [in,out] If non-null, source for the. + /// Length of the chunk. + /// The chunk offset. + /// The length. + /// + /// A SendError. + /// private protected abstract unsafe SendError SendTo(T arg0, int packetID, uint commandID, @@ -557,6 +555,18 @@ private protected abstract unsafe SendError SendTo(T arg0, int chunkOffset, int length); + /// + /// Sends to. + /// + /// Socket|Endpoint. + /// Identifier for the command. + /// The data. + /// The offset. + /// The length. + /// Identifier for the response. + /// + /// A SendError. + /// private unsafe SendError SendTo(T arg0, uint commandID, byte[] data, @@ -598,7 +608,7 @@ private unsafe SendError SendTo(T arg0, return SendError.Invalid; } - /// + /// public SendError SendTo(TServerClient client, uint commandID, byte[] data, @@ -609,7 +619,7 @@ public SendError SendTo(TServerClient client, return SendTo(client.Arg0, commandID, data, offset, length, responseID); } - /// + /// public SendError SendTo(TServerClient client, uint commandID, ISerializable serializable, @@ -619,7 +629,7 @@ public SendError SendTo(TServerClient client, return SendTo(client.Arg0, commandID, dataB, 0, length, responseID); } - /// + /// public SendError SendTo(TServerClient client, uint commandID, in T1 data, @@ -630,7 +640,7 @@ public SendError SendTo(TServerClient client, return SendTo(client.Arg0, commandID, dataB, 0, length, responseID); } - /// + /// public void SendToAll(uint commandID, byte[] data, int offset, int length) { Dictionary clients; @@ -654,7 +664,7 @@ public void SendToAll(uint commandID, byte[] data, int offset, int length) } } - /// + /// public void SendToAll(uint commandID, in T1 data) where T1 : unmanaged { @@ -662,7 +672,7 @@ public void SendToAll(uint commandID, in T1 data) SendToAll(commandID, buffer, 0, length); } - /// + /// public void SendToAll(uint commandID, ISerializable serializable) { byte[] buffer = serializable.Serialize(out int length); @@ -678,7 +688,7 @@ public void SendToAll(uint commandID, ISerializable serializable) /// private bool _disposed; - /// + /// public void Dispose() { Dispose(true); @@ -724,6 +734,9 @@ protected virtual void OnDispose(bool disposing) { } #endregion } + /// + /// A big data handler. + /// class BigDataHandler { /// @@ -809,14 +822,20 @@ internal unsafe byte[] Receive(int key, /// private struct Buffer { + /// + /// The data. + /// public readonly byte[] Data; + /// + /// The bytes left. + /// public int BytesLeft; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// - /// The data. - /// The bytes left. + /// The data. + /// The bytes left. public Buffer(byte[] data, int bytesLeft) { Data = data; diff --git a/network.wiki b/network.wiki index 5b88491..25d4051 160000 --- a/network.wiki +++ b/network.wiki @@ -1 +1 @@ -Subproject commit 5b884911bc6ce78d684c72fecda2dfc7c0ecfcc3 +Subproject commit 25d405168ce36652108fc4b3b5f71012153b175b