From: JamJar00 Date: Thu, 14 Apr 2016 15:04:56 +0000 (+0100) Subject: Added RUDP X-Git-Tag: 1.0.0~167 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=c1b66fa899f14cd2f7a602061a35514bfb2fe13e;p=rhonda%2Fimpostor.hazel.git Added RUDP --- diff --git a/Hazel.UnitTests/TcpConnectionTests.cs b/Hazel.UnitTests/TcpConnectionTests.cs index cd0bdf5..d0f50b5 100644 --- a/Hazel.UnitTests/TcpConnectionTests.cs +++ b/Hazel.UnitTests/TcpConnectionTests.cs @@ -11,7 +11,7 @@ namespace Hazel.UnitTests /// Tests the fields on TcpConnection. /// [TestMethod] - public void TcpConnectionFieldTest() + public void TcpFieldTest() { using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296)) using (TcpConnection connection = new TcpConnection()) @@ -35,12 +35,12 @@ namespace Hazel.UnitTests /// Tests sending and receiving on the TcpConnection. /// [TestMethod] - public void TcpConnectionSendReceiveTest() + public void TcpServerToClientTest() { using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296)) using (TcpConnection connection = new TcpConnection()) { - TestHelper.RunSendReceiveTest(listener, connection, 4, 0, 0); + TestHelper.RunServerToClientTest(listener, connection, 4, 0, 0, SendOption.OrderedFragmentedReliable); } } } diff --git a/Hazel.UnitTests/TestHelper.cs b/Hazel.UnitTests/TestHelper.cs index 3e53b0c..d5b190b 100644 --- a/Hazel.UnitTests/TestHelper.cs +++ b/Hazel.UnitTests/TestHelper.cs @@ -16,7 +16,8 @@ namespace Hazel.UnitTests /// /// The listener to test. /// The connection to test. - internal static void RunSendReceiveTest(ConnectionListener listener, Connection connection, int headerSize, int handshakeSize, int totalHandshakeSize) + //TODO both directions? + internal static void RunServerToClientTest(ConnectionListener listener, Connection connection, int headerSize, int handshakeSize, int totalHandshakeSize, SendOption sendOption) { //Setup meta stuff byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -25,7 +26,7 @@ namespace Hazel.UnitTests //Setup listener listener.NewConnection += delegate(object sender, NewConnectionEventArgs args) { - args.Connection.WriteBytes(data); + args.Connection.WriteBytes(data, sendOption); Assert.AreEqual(data.Length, args.Connection.Statistics.DataBytesSent); Assert.AreEqual(0, args.Connection.Statistics.DataBytesReceived); Assert.AreEqual(data.Length + headerSize, args.Connection.Statistics.TotalBytesSent); @@ -44,6 +45,8 @@ namespace Hazel.UnitTests Assert.AreEqual(data[i], args.Bytes[i]); } + Assert.AreEqual(sendOption, args.SendOption); + mutex.Set(); }; diff --git a/Hazel.UnitTests/UdpConnectionTests.cs b/Hazel.UnitTests/UdpConnectionTests.cs index 13b733f..b2dbcd5 100644 --- a/Hazel.UnitTests/UdpConnectionTests.cs +++ b/Hazel.UnitTests/UdpConnectionTests.cs @@ -11,7 +11,7 @@ namespace Hazel.UnitTests /// Tests the fields on UdpConnection. /// [TestMethod] - public void UdpConnectionFieldTest() + public void UdpFieldTest() { using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296)) using (UdpConnection connection = new UdpClientConnection()) @@ -32,15 +32,28 @@ namespace Hazel.UnitTests } /// - /// Tests sending and receiving on the UdpConnection. + /// Tests server to client unreliable communication on the UdpConnection. /// [TestMethod] - public void UdpConnectionSendReceiveTest() + public void UdpUnreliableServerToClientTest() { using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296)) using (UdpConnection connection = new UdpClientConnection()) { - TestHelper.RunSendReceiveTest(listener, connection, 1, 1, 2); + TestHelper.RunServerToClientTest(listener, connection, 1, 1, 2, SendOption.None); + } + } + + /// + /// Tests server to client reliable communication on the UdpConnection. + /// + [TestMethod] + public void UdpReliableServerToClientTest() + { + using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296)) + using (UdpConnection connection = new UdpClientConnection()) + { + TestHelper.RunServerToClientTest(listener, connection, 3, 1, 2, SendOption.Reliable); } } } diff --git a/Hazel/DataEventArgs.cs b/Hazel/DataEventArgs.cs index a68e2fd..9c87cf5 100644 --- a/Hazel/DataEventArgs.cs +++ b/Hazel/DataEventArgs.cs @@ -17,15 +17,21 @@ namespace Hazel /// /// The bytes received. /// - public byte[] Bytes; + public byte[] Bytes { get; private set; } + + /// + /// The SendOption the data was sent with. + /// + public object SendOption { get; private set; } /// /// Creates DataEventArgs from bytes received. /// /// - public DataEventArgs(byte[] bytes) + public DataEventArgs(byte[] bytes, SendOption sendOption) { this.Bytes = bytes; + this.SendOption = sendOption; } } } diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index dcad336..38f4f3d 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -55,7 +55,8 @@ - + + @@ -64,6 +65,7 @@ Code + diff --git a/Hazel/SendFlags.cs b/Hazel/SendFlags.cs deleted file mode 100644 index 27251ff..0000000 --- a/Hazel/SendFlags.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Hazel -{ - /// - /// Specifies how a message should be sent. - /// - [Flags] - public enum SendOption : byte - { - /// - /// Requests unreliable delivery with no framentation or ordering. - /// - None = 0, - - /// - /// Requests data be sent reliably. Data is guaranteed to arrive at it's destination. - /// - Reliable = 1, - - /// - /// Requests that data should be sent in order. - /// - /// - /// Any packets that are out of order in this option will be dropped. - /// - Ordered = 2, - - /// - /// Requests that data should be sent in order and reliably. - /// - /// - /// Only messages that are sent using OrderedReliable or OrderedFragmentedReliable will arrive - /// in order, other messages - /// may arrive in between. - /// - OrderedReliable = 3, - - /// - /// Requests data be sent so that large messages are fragmented into smaller chunks of - /// data and reassembled when received. - /// - FragmentedReliable = 5, - - /// - /// Requests data be sent so that large messages are fragmented into smaller chunks of data and - /// reassembled when received and that the message arrives in order with other messages. - /// - OrderedFragmentedReliable = 7 - } -} diff --git a/Hazel/SendOption.cs b/Hazel/SendOption.cs new file mode 100644 index 0000000..27251ff --- /dev/null +++ b/Hazel/SendOption.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hazel +{ + /// + /// Specifies how a message should be sent. + /// + [Flags] + public enum SendOption : byte + { + /// + /// Requests unreliable delivery with no framentation or ordering. + /// + None = 0, + + /// + /// Requests data be sent reliably. Data is guaranteed to arrive at it's destination. + /// + Reliable = 1, + + /// + /// Requests that data should be sent in order. + /// + /// + /// Any packets that are out of order in this option will be dropped. + /// + Ordered = 2, + + /// + /// Requests that data should be sent in order and reliably. + /// + /// + /// Only messages that are sent using OrderedReliable or OrderedFragmentedReliable will arrive + /// in order, other messages + /// may arrive in between. + /// + OrderedReliable = 3, + + /// + /// Requests data be sent so that large messages are fragmented into smaller chunks of + /// data and reassembled when received. + /// + FragmentedReliable = 5, + + /// + /// Requests data be sent so that large messages are fragmented into smaller chunks of data and + /// reassembled when received and that the message arrives in order with other messages. + /// + OrderedFragmentedReliable = 7 + } +} diff --git a/Hazel/SendOptionInternal.cs b/Hazel/SendOptionInternal.cs new file mode 100644 index 0000000..335d581 --- /dev/null +++ b/Hazel/SendOptionInternal.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hazel +{ + /// + /// Extra internal states for SendOption enumeration. + /// + enum SendOptionInternal : byte + { + Acknowledgement = 255 + } +} diff --git a/Hazel/TcpConnection.cs b/Hazel/TcpConnection.cs index 5843491..4ad2db2 100644 --- a/Hazel/TcpConnection.cs +++ b/Hazel/TcpConnection.cs @@ -177,7 +177,7 @@ namespace Hazel Statistics.LogReceive(bytes.Length, bytes.Length + 4); //Fire DataReceived event - InvokeDataReceived(new DataEventArgs(bytes)); + InvokeDataReceived(new DataEventArgs(bytes, SendOption.OrderedFragmentedReliable)); } /// diff --git a/Hazel/UdpClientConnection.cs b/Hazel/UdpClientConnection.cs index f9fc59b..fcdc917 100644 --- a/Hazel/UdpClientConnection.cs +++ b/Hazel/UdpClientConnection.cs @@ -36,14 +36,19 @@ namespace Hazel /// The option this data is requested to send with. public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None) { - //Add sendflag byte to start - byte[] fullBytes = new byte[bytes.Length + 1]; - fullBytes[0] = (byte)sendOption; - Buffer.BlockCopy(bytes, 0, fullBytes, 1, bytes.Length); + //Add header information and send + HandleSend(bytes, sendOption); + } + /// + /// Writes bytes to the socket. + /// + /// The bytes to send. + protected override void WriteBytesToConnection(byte[] bytes) + { //Pack SocketAsyncEventArgs args = new SocketAsyncEventArgs(); - args.SetBuffer(fullBytes, 0, fullBytes.Length); + args.SetBuffer(bytes, 0, bytes.Length); args.RemoteEndPoint = RemoteEndPoint; lock (socket) @@ -67,8 +72,6 @@ namespace Hazel throw he; } } - - Statistics.LogSend(bytes.Length, fullBytes.Length); } /// @@ -120,7 +123,7 @@ namespace Hazel } //Write bytes to the server to tell it hi (and to punch a hole in our NAT, if present). - WriteBytes(new byte[] { 0 }, SendOption.Reliable); + WriteBytes(new byte[] { 0 }, SendOption.None); //TODO special hello message } /// @@ -163,9 +166,11 @@ namespace Hazel return; } - //Copy to new buffer - byte[] buffer = new byte[bytesReceived]; - Buffer.BlockCopy((byte[])result.AsyncState, 1, buffer, 0, bytesReceived - 1); + //Decode the data received + byte[] buffer = HandleReceive(dataBuffer, bytesReceived); + SendOption sendOption = (SendOption)dataBuffer[0]; + + //TODO may possibly get better performance with above/below swapped and block copy added //Begin receiving again try @@ -177,10 +182,9 @@ namespace Hazel { HandleDisconnect(new HazelException("A Socket exception occured while initiating a receive operation.", e)); } - - Statistics.LogReceive(buffer.Length - 1, buffer.Length); - - InvokeDataReceived(new DataEventArgs(buffer)); + + if (buffer != null) + InvokeDataReceived(new DataEventArgs(buffer, sendOption)); } /// diff --git a/Hazel/UdpConnection.cs b/Hazel/UdpConnection.cs index 5c1b9ed..09936fb 100644 --- a/Hazel/UdpConnection.cs +++ b/Hazel/UdpConnection.cs @@ -18,73 +18,84 @@ namespace Hazel /// /// Represents a connection that uses the UDP protocol. /// - public abstract class UdpConnection : Connection + public abstract partial class UdpConnection : Connection { /// - /// The packets of data that have been transmitted reliably and not acknowledged. + /// The remote end point of this connection. /// - Dictionary reliableDataPacketsSent = new Dictionary(); + public EndPoint RemoteEndPoint { get; protected set; } /// - /// Holds the last ID allocated. + /// Writes the given bytes to the connection. /// - volatile uint lastIDAllocated; + /// The bytes to write. + protected abstract void WriteBytesToConnection(byte[] bytes); /// - /// The remote end point of this connection. + /// Handles the reliable/fragmented/ordered sending from this connection. /// - public EndPoint RemoteEndPoint { get; protected set; } - - class Packet + /// The data being sent. + /// The send option. + /// The bytes that should actually be sent. + protected void HandleSend(byte[] data, SendOption sendOption) { - public byte[] Data; - public DateTime SentTime; - - public Packet(byte[] data, DateTime sentTime) + byte[] bytes; + switch (sendOption) { - Data = data; - SentTime = sentTime; + case SendOption.Reliable: + bytes = new byte[data.Length + 3]; + WriteReliableSendHeader(bytes); + break; + + default: + bytes = new byte[data.Length + 1]; + break; } + + //Add message type + bytes[0] = (byte)sendOption; + + //Copy data into new array + Buffer.BlockCopy(data, 0, bytes, bytes.Length - data.Length, data.Length); + + //Write to connection + WriteBytesToConnection(bytes); + + Statistics.LogSend(data.Length, bytes.Length); } /// - /// Handles the reliable/fragmented/ordered sending from this connection. + /// Handles the receiving of data. /// - /// The data being sent. - /// The send option. - /// The bytes that should actually be sent. - protected byte[] HandleSend(byte[] data, SendOption sendOption) + /// The array of the data received. + /// The number of bytes that were received. + /// The bytes of data received. + protected byte[] HandleReceive(byte[] buffer, int bytesReceived) { - byte[] bytes = new byte[data.Length + 1]; - int offset = 1; - - if (sendOption == SendOption.Reliable) + int headerSize = 1; + switch (buffer[0]) { - bytes = new byte[data.Length + 5]; - offset = 5; - - lock (reliableDataPacketsSent) - { - //Find an ID not used yet. - uint id; + case (byte)SendOption.Reliable: + headerSize = 3; - do - id = ++lastIDAllocated; - while (reliableDataPacketsSent.ContainsKey(id)); + if (HandleReliableReceive(buffer) == false) + return null; + break; - bytes[1] = (byte)(id & 0xFF); - bytes[2] = (byte)((id >> 16) & 0xFF); - bytes[3] = (byte)((id >> 8) & 0xFF); - bytes[4] = (byte)id; - - //Remember packet - reliableDataPacketsSent.Add(id, new Packet(data, DateTime.Now)); - } + case (byte)SendOptionInternal.Acknowledgement: + HandleAcknowledgement(buffer); + + Statistics.LogReceive(0, bytesReceived); + + return null; } - Buffer.BlockCopy(data, 0, bytes, offset, bytes.Length); + byte[] dataBytes = new byte[bytesReceived - headerSize]; + Buffer.BlockCopy(buffer, headerSize, dataBytes, 0, dataBytes.Length); + + Statistics.LogReceive(dataBytes.Length, bytesReceived); - return bytes; + return dataBytes; } } } diff --git a/Hazel/UdpServerConnection.cs b/Hazel/UdpServerConnection.cs index ebf255f..40dc48d 100644 --- a/Hazel/UdpServerConnection.cs +++ b/Hazel/UdpServerConnection.cs @@ -46,20 +46,22 @@ namespace Hazel /// The option this data is requested to send with. public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None) { - //Add sendflag byte to start - byte[] fullBytes = new byte[bytes.Length + 1]; - fullBytes[0] = (byte)sendOption; - Buffer.BlockCopy(bytes, 0, fullBytes, 1, bytes.Length); + HandleSend(bytes, sendOption); + } + /// + /// Writes bytes to the listener to send. + /// + /// bytes to send. + protected override void WriteBytesToConnection(byte[] bytes) + { lock (stateLock) { if (State != ConnectionState.Connected) throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?"); - Listener.SendData(fullBytes, RemoteEndPoint); + Listener.SendData(bytes, RemoteEndPoint); } - - Statistics.LogSend(bytes.Length, fullBytes.Length); } /// @@ -79,12 +81,10 @@ namespace Hazel /// internal void InvokeDataReceived(byte[] buffer) { - byte[] data = new byte[buffer.Length - 1]; - Buffer.BlockCopy(buffer, 1, data, 0, data.Length); + byte[] data = HandleReceive(buffer, buffer.Length); - Statistics.LogReceive(data.Length, buffer.Length); - - InvokeDataReceived(new DataEventArgs(data)); + if (data != null) + InvokeDataReceived(new DataEventArgs(data, (SendOption)data[0])); } ///