Compare commits

..

7 Commits

Author SHA1 Message Date
ece0dffd85 Implementing Packet Framing
Surrounding packets with Int32 6655 to identify packet structure
2021-12-16 16:41:29 +10:00
a9bc8c4e7d Created and implemented Packet abstract class 2021-12-15 15:17:00 +10:00
cdda6c0c8a Start on Server & Client Events
Created ClientConnected event
Created DataReceived event
2021-12-15 15:14:05 +10:00
fc1195c446 Started on Events & Connection Handling
Events
- Created OnClientConnected
Connection Handling
- Started on async client connection handling, firing event
2021-12-13 15:43:28 +10:00
87a569ee9e Created Manager Initializer & Started on Packet Serializing/Deserializing
Manager Initializer initializes all managers found inheriting the IManager interface
Packet Manager
- Created LoadPacketInfo which gets PropertyInfo using reflection, and caches the returned information
- Started on Send<Packet>(Packet)
- Started on serializing packets to stream
- Started on packet deserializer
Created PacketIgnore attribute to ignore properties in a packet when serializing
2021-12-06 15:18:29 +10:00
2e9a712e71 Initial Server & Client Commit
Started on Interfaces
- Created IManager
- Created IPacket
Started on Managers
- Created Packet Manager
Started on async client/server implementation
2021-12-03 15:42:14 +10:00
46405a8cf4 Initial Visual Studio Commit 2021-12-03 15:40:09 +10:00
12 changed files with 819 additions and 0 deletions

59
Articulate-Network.csproj Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{36A8EC7B-B239-4744-892A-4FE633511E8D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Articulate_Network</RootNamespace>
<AssemblyName>Articulate-Network</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\PacketIgnore.cs" />
<Compile Include="Client.cs" />
<Compile Include="Events\ClientConnectedEventArgs.cs" />
<Compile Include="Events\DataReceivedEventArgs.cs" />
<Compile Include="Interfaces\IManager.cs" />
<Compile Include="Interfaces\IPacket.cs" />
<Compile Include="Managers\PacketManager.cs" />
<Compile Include="Packets\Packet.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Enums\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

25
Articulate-Network.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31624.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Articulate-Network", "Articulate-Network.csproj", "{36A8EC7B-B239-4744-892A-4FE633511E8D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{36A8EC7B-B239-4744-892A-4FE633511E8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{36A8EC7B-B239-4744-892A-4FE633511E8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36A8EC7B-B239-4744-892A-4FE633511E8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36A8EC7B-B239-4744-892A-4FE633511E8D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81DCC61E-0253-4356-8FBF-8DF05F4B7FA1}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Attributes
{
public class PacketIgnore : Attribute
{
}
}

180
Client.cs Normal file
View File

@ -0,0 +1,180 @@
using Articulate_Network.Interfaces;
using Articulate_Network.Managers;
using Articulate_Network.Packets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network
{
public class Client
{
List<IManager> managers = new List<IManager>();
int bufferSize = 16384;
TcpClient tcp;
public Client()
{
InitializeManagers();
tcp = new TcpClient()
{
SendBufferSize = bufferSize,
ReceiveBufferSize = bufferSize
};
}
public async Task<TcpClient> Connect(string ip, int port)
{
return await Task.Run(() =>
{
try
{
tcp.Connect(ip, port);
tcp.SendBufferSize = bufferSize;
tcp.ReceiveBufferSize = bufferSize;
_ = ReceiveData();
return tcp;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return tcp;
}
}
);
}
public async Task<bool> SendPacket(Packet packet)
{
await Task.Run(() =>
{
byte[] buffer = Get<PacketManager>().SerializePacket(packet);
tcp.Client.Send(buffer, buffer.Length, SocketFlags.None);
});
return false;
}
public async Task<bool> ReceiveData()
{
await Task.Run(() =>
{
byte[] buffer = new byte[bufferSize];
int offset = 0;
while (true)
{
int bytesToRead = tcp.Client.Receive(buffer, offset, buffer.Length - offset, SocketFlags.None);
if (offset > 0)
offset = 0;
var ms = new MemoryStream(buffer);
var br = new BinaryReader(ms);
while (ms.Position < ms.Length)
{
if (ms.Position + 4 > ms.Length)
break;
int magic = br.ReadInt32();
if (magic != 6655)
{
//Console.WriteLine("Not the start of a packet.");
continue;
}
int length = (int)br.ReadInt64();
if (ms.Position + length > ms.Length) // Received partial packet data, store partial data and continue
{
ms.Position -= 12;
offset = (int)(ms.Length - ms.Position);
//Console.WriteLine($"Partial data. Offset: {offset}");
buffer = new byte[bufferSize];
ms.Read(buffer, 0, offset);
break;
}
ms.Position += length;
int end = (int)br.ReadInt64();
ms.Position -= 8;
ms.Position -= length;
if (length == end && length > 1) // Valid packet, trigger event
{
var p = Get<PacketManager>().DeserializePacket(ms);
OnDataReceived(new Articulate_Network.Events.DataReceivedEventArgs()
{
Packet = p
});
}
else
{
Console.WriteLine($"Not a valid packet. Length: {length} - End: {end} - Offset: {offset} - Position: {ms.Position}");
}
}
}
});
return false;
}
public void InitializeManagers()
{
var @interface = typeof(IManager);
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var managersFound = assembly.GetTypes().Where(t => @interface.IsAssignableFrom(t) && !t.IsAbstract);
foreach (var m in managersFound)
{
var manager = (IManager)Activator.CreateInstance(m);
manager.Initialize();
managers.Add(manager);
}
}
Console.WriteLine($"Initialized {managers.Count} manager{(managers.Count > 1 ? "s" : "")}");
}
public T Get<T>()
{
return (T)managers.SingleOrDefault(t => typeof(T) == t.GetType());
}
protected virtual void OnDataReceived(Articulate_Network.Events.DataReceivedEventArgs e)
{
EventHandler<Articulate_Network.Events.DataReceivedEventArgs> handler = DataReceived;
if (handler != null)
handler(this, e);
}
public event EventHandler<Articulate_Network.Events.DataReceivedEventArgs> DataReceived;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Events
{
public class ClientConnectedEventArgs : EventArgs
{
public TcpClient TcpClient { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using Articulate_Network.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Events
{
public class DataReceivedEventArgs : EventArgs
{
public TcpClient Client { get; set; }
public Packet Packet { get; set; }
}
}

13
Interfaces/IManager.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Interfaces
{
public interface IManager
{
void Initialize();
}
}

13
Interfaces/IPacket.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Interfaces
{
public interface IPacket
{
}
}

185
Managers/PacketManager.cs Normal file
View File

@ -0,0 +1,185 @@
using Articulate_Network.Attributes;
using Articulate_Network.Interfaces;
using Articulate_Network.Packets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Managers
{
public class PacketManager : IManager
{
Dictionary<Type, List<PropertyInfo>> packetInfo = new Dictionary<Type, List<PropertyInfo>>();
Dictionary<string, Packet> packetInstances = new Dictionary<string, Packet>();
public void Initialize()
{
var @interface = typeof(IPacket);
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var packetsFound = assembly.GetTypes().Where(t => @interface.IsAssignableFrom(t) && !t.IsAbstract);
foreach (var p in packetsFound)
{
var packet = (Packet)Activator.CreateInstance(p);
packetInstances.Add(p.Name, packet);
LoadPacketInfo(packet);
}
}
Console.WriteLine($"Registered {packetInstances.Count} packet{(packetInstances.Count == 1 ? "" : "s")}");
}
public void LoadPacketInfo(IPacket packet)
{
packetInfo.Add(packet.GetType(), packet.GetType().GetProperties().Where(f => !Attribute.IsDefined(f, typeof(PacketIgnore))).OrderBy(f => f.Name).ToList());
}
public void Send(Packet packet)
{
Stopwatch watch = new Stopwatch();
watch.Start();
if (!packetInfo.ContainsKey(packet.GetType()))
LoadPacketInfo(packet);
watch.Stop();
Console.WriteLine($"Reflection time: {watch.ElapsedTicks / 10} microseconds");
//send packet
var serialized = SerializePacket(packet);
//var deserialized = DeserializePacket<T>(serialized);
//return (T)deserialized;
}
public byte[] SerializePacket(Packet packet)
{
if (!packetInfo.ContainsKey(packet.GetType()))
LoadPacketInfo(packet);
Stopwatch watch = new Stopwatch();
watch.Start();
var fields = packetInfo[packet.GetType()];
MemoryStream buffer = new MemoryStream();
BinaryWriter binaryWriter = new BinaryWriter(buffer);
binaryWriter.Write(packet.GetType().Name);
foreach (var field in fields)
{
if (field.PropertyType == typeof(bool))
binaryWriter.Write((bool)field.GetValue(packet));
if (field.PropertyType == typeof(int))
binaryWriter.Write((int)field.GetValue(packet));
if (field.PropertyType == typeof(long))
binaryWriter.Write((long)field.GetValue(packet));
if (field.PropertyType == typeof(string))
binaryWriter.Write(((string)field.GetValue(packet)) ?? string.Empty);
if (field.PropertyType.IsEnum)
binaryWriter.Write((int)field.GetValue(packet));
if (field.PropertyType == typeof(byte[]))
{
byte[] buf = (byte[])field.GetValue(packet);
binaryWriter.Write(buf.Length);
binaryWriter.Write(buf);
}
//Console.WriteLine(field.Name);
}
byte[] packetData = buffer.ToArray();
long length = packetData.Length;
buffer.SetLength(0);
binaryWriter.Write(6655);
binaryWriter.Write(length);
binaryWriter.Write(packetData);
binaryWriter.Write(length);
//Console.WriteLine("Sent bytes: " + length);
watch.Stop();
//Console.WriteLine($"Serialize time: {watch.ElapsedTicks / 10} microseconds");
return buffer.ToArray();
}
public Packet DeserializePacket(MemoryStream stream)
{
Stopwatch watch = new Stopwatch();
watch.Start();
BinaryReader reader = new BinaryReader(stream);
string typeName = reader.ReadString();
//Console.WriteLine(typeName);
Packet packet = packetInstances[typeName];/*(IPacket)Activator.CreateInstance(typeof(T));*/
var fields = packetInfo[packet.GetType()];
//Console.WriteLine($"Stream length: {stream.Length}");
//Console.WriteLine($"Stream position: {stream.Position}");
foreach (var field in fields)
{
if (field.PropertyType == typeof(bool))
field.SetValue(packet, reader.ReadBoolean());
if (field.PropertyType == typeof(int))
field.SetValue(packet, reader.ReadInt32());
if (field.PropertyType == typeof(long))
field.SetValue(packet, reader.ReadInt64());
if (field.PropertyType == typeof(string))
field.SetValue(packet, reader.ReadString());
if (field.PropertyType.IsEnum)
field.SetValue(packet, reader.ReadInt32());
if (field.PropertyType == typeof(byte[]))
{
int length = reader.ReadInt32();
field.SetValue(packet, reader.ReadBytes(length));
}
}
reader.ReadInt64();
watch.Stop();
//Console.WriteLine($"Deserialize time: {watch.ElapsedTicks / 10} microseconds");
return packet;
}
public Packet DeserializePacket(byte[] bytes)
{
return DeserializePacket(new MemoryStream(bytes));
}
public void HandlePacket(Packet packet)
{
Console.WriteLine(packet.GetType().Name);
}
}
}

14
Packets/Packet.cs Normal file
View File

@ -0,0 +1,14 @@
using Articulate_Network.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network.Packets
{
public abstract class Packet : IPacket
{
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
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("Articulate-Network")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Queensland Government")]
[assembly: AssemblyProduct("Articulate-Network")]
[assembly: AssemblyCopyright("Copyright © Queensland Government 2021")]
[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("36a8ec7b-b239-4744-892a-4fe633511e8d")]
// 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")]

250
Server.cs Normal file
View File

@ -0,0 +1,250 @@
using Articulate_Network.Events;
using Articulate_Network.Interfaces;
using Articulate_Network.Managers;
using Articulate_Network.Packets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Articulate_Network
{
public class Server
{
List<IManager> managers = new List<IManager>();
TcpClient[] clients = new TcpClient[300];
TcpListener tcpListener;
public Server()
{
InitializeManagers();
}
public async Task<bool> Start(string ip, int port)
{
tcpListener = new TcpListener(IPAddress.Parse(ip), port);
bool connected = await Task.Run(async () =>
{
try
{
tcpListener.Start();
_ = HandleConnections();
return true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
}
);
return false;
}
public async Task HandleConnections()
{
while (true)
{
var client = await tcpListener.AcceptTcpClientAsync();
for (int i = 0; i < clients.Length; i++)
if (clients[i] == null)
{
clients[i] = client;
Console.WriteLine($"Client {i + 1} connected from {(client.Client.RemoteEndPoint as IPEndPoint).Address}.");
break;
}
_ = ReceiveData(client);
OnClientConnected(new ClientConnectedEventArgs() { TcpClient = client });
}
}
/*public async Task<bool> ReceiveData(TcpClient client)
{
await Task.Run(() =>
{
byte[] buffer = new byte[8192];
var ms = new MemoryStream(buffer);
var br = new BinaryReader(ms);
while (client.Connected)
{
try
{
int bytesToRead = client.Client.Receive(buffer, buffer.Length, SocketFlags.None);
ms.Position = 0;
while (ms.Position < bytesToRead)
{
int length = (int)br.ReadInt64();
OnDataReceived(new Articulate_Network.Events.DataReceivedEventArgs()
{
Packet = Get<PacketManager>().DeserializePacket(ms),
Client = client
});
int end = (int)br.ReadInt64();
}
}
catch (SocketException e)
{
}
}
});
return false;
}*/
public async Task<bool> ReceiveData(TcpClient client)
{
await Task.Run(() =>
{
byte[] buffer = new byte[16384];
int offset = 0;
while (client.Connected)
{
try
{
int bytesToRead = client.Client.Receive(buffer, offset, buffer.Length - offset, SocketFlags.None);
if (bytesToRead == 0)
{
Console.WriteLine("No data.");
continue;
}
var ms = new MemoryStream(buffer);
var br = new BinaryReader(ms);
if (offset > 0)
offset = 0;
while (ms.Position < ms.Length)
{
br.ReadInt32();
int length = (int)br.ReadInt64();
if (length < 0)
break;
if (ms.Position + length > ms.Length)
{
ms.Position -= 8;
buffer = new byte[16384];
offset = (int)(ms.Length - ms.Position);
ms.Read(buffer, 0, offset);
break;
}
ms.Position += length;
int end = (int)br.ReadInt64();
ms.Position -= 8;
if (length == end && length < 1)
break;
ms.Position -= length;
var p = Get<PacketManager>().DeserializePacket(ms);
OnDataReceived(new Articulate_Network.Events.DataReceivedEventArgs()
{
Packet = p,
Client = client
});
br.ReadInt64();
}
}
catch (SocketException e)
{
Console.WriteLine("Client disconnected.");
}
}
});
return false;
}
public async Task<bool> SendPacket(TcpClient tcpClient, Packet packet)
{
await Task.Run(() =>
{
byte[] buffer = Get<PacketManager>().SerializePacket(packet);
int sentBytes = tcpClient.Client.Send(buffer, buffer.Length, SocketFlags.None);
if (sentBytes == 0)
Console.WriteLine($"Sent: {sentBytes}");
});
return false;
}
public void InitializeManagers()
{
var @interface = typeof(IManager);
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var managersFound = assembly.GetTypes().Where(t => @interface.IsAssignableFrom(t) && !t.IsAbstract);
foreach (var m in managersFound)
{
var manager = (IManager)Activator.CreateInstance(m);
manager.Initialize();
managers.Add(manager);
}
}
Console.WriteLine($"Initialized {managers.Count} manager{(managers.Count > 1 ? "s" : "")}");
}
public T Get<T>()
{
return (T)managers.SingleOrDefault(t => typeof(T) == t.GetType());
}
protected virtual void OnClientConnected(ClientConnectedEventArgs e)
{
EventHandler<ClientConnectedEventArgs> handler = ClientConnected;
if (handler != null)
handler(this, e);
}
public event EventHandler<ClientConnectedEventArgs> ClientConnected;
protected virtual void OnDataReceived(Articulate_Network.Events.DataReceivedEventArgs e)
{
EventHandler<Articulate_Network.Events.DataReceivedEventArgs> handler = DataReceived;
if (handler != null)
handler(this, e);
}
public event EventHandler<Articulate_Network.Events.DataReceivedEventArgs> DataReceived;
}
}