Skip to content

Commit

Permalink
feat: Added fragmentation, unconnectedMessages and a hashcash issue (#3)
Browse files Browse the repository at this point in the history
* feat: Added fragmentation

* Added fragmented channel

* fix: Fixed various fragmentation issues

* Finalized ReliableSequencedFragmented channel

* Pooled pointer arrays

* Fixed fragment resending

* Added unit tests

* Adjusted defaults and added validation to SocketConfig

* Updated readme

* Removed old comment

* Added print when an invalid channel got a message

* fix: Fixes hashcash difficulty

* Updated socketConfig to use more reasonable defaults

* Reduced example difficulty

* feat: Added unconnected messages

* Fixed compilation
  • Loading branch information
TwoTenPvP authored Sep 3, 2019
1 parent fd4a9fd commit 848ffb6
Show file tree
Hide file tree
Showing 31 changed files with 1,622 additions and 169 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ There are currently a few ways of sending messages in Ruffles. The types are:
All messages are guaranteed to be delivered, the order is not guaranteed, duplicates are dropped. Uses a fixed sliding window.
#### ReliableSequenced
All messages are guaranteed to be delivered with the order also being guaranteed, duplicates are dropped. Uses a fixed sliding window.
#### ReliableSequencedFragmented
All messages are guaranteed to be delivered with the order also being guaranteed, duplicates are dropped. Uses a fixed sliding window. Allows large messages to be fragmented.
#### Unreliable
Delivery is not guaranteed, nor is the order. Duplicates are dropped.
#### UnreliableSequenced
Delivery is not guaranteed but the order is. Older packets and duplicate packets are dropped.
#### UnreliableRaw
Delivery is not guaranteed nor is the order. Duplicates are not dropped.
#### UnconnectedMessages
Raw UDP packets that does not require a connection.

### Threading
Ruffles can run in many different threading environments, it can be run passively single threaded, actively single threaded, or in a threaded environment where everything is done via message queues while remaining garbage free.
Expand All @@ -63,9 +67,8 @@ Small packets will be delayed for sending, this allows them to be merged into on
This is stuff I want to and plan to add

* Path MTU
* Fragmentation
* More Fragmentation Types
* Explicit Nack
* Layer 4 DOS Prevention with an on-connect HashCash challenge
* Reliable StateUpdate / LastPacket channel
* MLAPI.Relay Support
* MLAPI.NAT (Holepuncher) support
Expand Down
41 changes: 32 additions & 9 deletions Ruffles.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,36 @@ public class Program
{
internal static readonly SocketConfig ServerConfig = new SocketConfig()
{
ChallengeDifficulty = 25, // Difficulty 25 is fairly hard
ChallengeDifficulty = 20, // Difficulty 20 is fairly hard
ChannelTypes = new ChannelType[]
{
ChannelType.Reliable,
ChannelType.ReliableSequenced,
ChannelType.Unreliable,
ChannelType.UnreliableSequenced
ChannelType.UnreliableSequenced,
ChannelType.ReliableSequencedFragmented
},
DualListenPort = 5674
DualListenPort = 5674,
SimulatorConfig = new Simulation.SimulatorConfig()
{
DropPercentage = 0.1f,
MaxLatency = 1000,
MinLatency = 300
},
UseSimulator = true
};

internal static readonly SocketConfig ClientConfig = new SocketConfig()
{
ChallengeDifficulty = 25, // Difficulty 25 is fairly hard
DualListenPort = 0 // Port 0 means we get a port by the operating system
ChallengeDifficulty = 20, // Difficulty 20 is fairly hard
DualListenPort = 0, // Port 0 means we get a port by the operating system
SimulatorConfig = new Simulation.SimulatorConfig()
{
DropPercentage = 0.1f,
MaxLatency = 1000,
MinLatency = 300
},
UseSimulator = true
};


Expand Down Expand Up @@ -112,8 +127,11 @@ private static void NoRufflesManager()

if (clientEvent.Type == NetworkEventType.Data)
{
Console.WriteLine("IsCorrect: " + (clientEvent.Data.Count == (1024 + 1024 * (messagesReceived % 5))) + ", Expected: " + (1024 + 1024 * (messagesReceived % 5)) + ", got: " + clientEvent.Data.Count);

messagesReceived++;
Console.WriteLine("Got message: \"" + Encoding.ASCII.GetString(clientEvent.Data.Array, clientEvent.Data.Offset, clientEvent.Data.Count) + "\"");
//Console.WriteLine("Got message of size: " + clientEvent.Data.Count);
//Console.WriteLine("Got message: \"" + Encoding.ASCII.GetString(clientEvent.Data.Array, clientEvent.Data.Offset, clientEvent.Data.Count) + "\"");
clientEvent.Recycle();
}

Expand All @@ -124,12 +142,17 @@ private static void NoRufflesManager()
}

if ((DateTime.Now - started).TotalSeconds > 10 && (DateTime.Now - lastSent).TotalSeconds >= 1)
{
{
Console.WriteLine("Sending size: " + (1024 + (1024 * (messageCounter % 5))));
byte[] largeFragment = new byte[1024 + 1024 * (messageCounter % 5)];


byte[] helloReliable = Encoding.ASCII.GetBytes("This message was sent over a reliable channel" + messageCounter);
byte[] helloReliableSequenced = Encoding.ASCII.GetBytes("This message was sent over a reliable sequenced channel" + messageCounter);

server.Send(new ArraySegment<byte>(helloReliableSequenced, 0, helloReliableSequenced.Length), clientId, 0, false);
server.Send(new ArraySegment<byte>(helloReliable, 0, helloReliable.Length), clientId, 1, false);
//server.Send(new ArraySegment<byte>(helloReliableSequenced, 0, helloReliableSequenced.Length), clientId, 0, false);
//server.Send(new ArraySegment<byte>(helloReliable, 0, helloReliable.Length), clientId, 1, false);
server.Send(new ArraySegment<byte>(largeFragment, 0, largeFragment.Length), clientId, 4, false);

messageCounter++;
lastSent = DateTime.Now;
Expand Down
108 changes: 108 additions & 0 deletions Ruffles.Tests/Channels/ReliableSequencedChannelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using NUnit.Framework;
using Ruffles.Channeling.Channels;
using Ruffles.Memory;
using Ruffles.Tests.Helpers;
using Ruffles.Tests.Stubs;
using System;

namespace Ruffles.Tests.Channels
{
[TestFixture()]
public class ReliableSequencedChannelTests
{
[Test()]
public void TestSimpleMessage()
{
Configuration.SocketConfig config = new Configuration.SocketConfig();

MemoryManager memoryManager = new MemoryManager(config);

ConnectionStub clientsConnectionToServer = new ConnectionStub();
ConnectionStub serversConnectionToClient = new ConnectionStub();

ReliableSequencedChannel clientChannel = new ReliableSequencedChannel(0, clientsConnectionToServer, config, memoryManager);
ReliableSequencedChannel serverChannel = new ReliableSequencedChannel(0, serversConnectionToClient, config, memoryManager);

byte[] message = BufferHelper.GetRandomBuffer(1024);

HeapMemory messageMemory = clientChannel.CreateOutgoingMessage(new ArraySegment<byte>(message, 0, 1024), out bool dealloc)[0];
ArraySegment<byte>? payload = serverChannel.HandleIncomingMessagePoll(new ArraySegment<byte>(messageMemory.Buffer, (int)messageMemory.VirtualOffset + 2, (int)messageMemory.VirtualCount - 2), out bool hasMore);

Assert.NotNull(payload);
Assert.False(hasMore);

byte[] bytePayload = new byte[payload.Value.Count];
Array.Copy(payload.Value.Array, payload.Value.Offset, bytePayload, 0, payload.Value.Count);

Assert.AreEqual(message, bytePayload);
}

[Test()]
public void TestOutOfOrder()
{
Configuration.SocketConfig config = new Configuration.SocketConfig();

MemoryManager memoryManager = new MemoryManager(config);

ConnectionStub clientsConnectionToServer = new ConnectionStub();
ConnectionStub serversConnectionToClient = new ConnectionStub();

ReliableSequencedChannel clientChannel = new ReliableSequencedChannel(0, clientsConnectionToServer, config, memoryManager);
ReliableSequencedChannel serverChannel = new ReliableSequencedChannel(0, serversConnectionToClient, config, memoryManager);

// Create 3 payloads
byte[] message1 = BufferHelper.GetRandomBuffer(1024);
byte[] message2 = BufferHelper.GetRandomBuffer(1024);
byte[] message3 = BufferHelper.GetRandomBuffer(1024);

// Sequence all payloads as outgoing
HeapMemory message1Memory = clientChannel.CreateOutgoingMessage(new ArraySegment<byte>(message1, 0, 1024), out bool dealloc)[0];
HeapMemory message2Memory = clientChannel.CreateOutgoingMessage(new ArraySegment<byte>(message2, 0, 1024), out dealloc)[0];
HeapMemory message3Memory = clientChannel.CreateOutgoingMessage(new ArraySegment<byte>(message3, 0, 1024), out dealloc)[0];

// Consume 1st payload
ArraySegment<byte>? payload1 = serverChannel.HandleIncomingMessagePoll(new ArraySegment<byte>(message1Memory.Buffer, (int)message1Memory.VirtualOffset + 2, (int)message1Memory.VirtualCount - 2), out bool hasMore1);
// Consume 3rd payload
ArraySegment<byte>? payload3 = serverChannel.HandleIncomingMessagePoll(new ArraySegment<byte>(message3Memory.Buffer, (int)message3Memory.VirtualOffset + 2, (int)message3Memory.VirtualCount - 2), out bool hasMore3);
// Consume 2nd payload
ArraySegment<byte>? payload2 = serverChannel.HandleIncomingMessagePoll(new ArraySegment<byte>(message2Memory.Buffer, (int)message2Memory.VirtualOffset + 2, (int)message2Memory.VirtualCount - 2), out bool hasMore2);

HeapMemory pollMemory = serverChannel.HandlePoll();

{
Assert.NotNull(payload1);
Assert.False(hasMore1);

byte[] bytePayload = new byte[payload1.Value.Count];
Array.Copy(payload1.Value.Array, payload1.Value.Offset, bytePayload, 0, payload1.Value.Count);

Assert.AreEqual(message1, bytePayload);
}

{
Assert.Null(payload3);
Assert.False(hasMore3);
}

{
Assert.NotNull(payload2);
Assert.True(hasMore2);

byte[] bytePayload = new byte[payload2.Value.Count];
Array.Copy(payload2.Value.Array, payload2.Value.Offset, bytePayload, 0, payload2.Value.Count);

Assert.AreEqual(message2, bytePayload);
}

{
// Check for the third packet
Assert.NotNull(pollMemory);

byte[] bytePayload = new byte[pollMemory.VirtualCount];
Array.Copy(pollMemory.Buffer, pollMemory.VirtualOffset, bytePayload, 0, pollMemory.VirtualCount);

Assert.AreEqual(message3, bytePayload);
}
}
}
}
Loading

0 comments on commit 848ffb6

Please sign in to comment.