websock refactor

changed from a static delegate system to a managed instance template
system
This commit is contained in:
Malloc of Kuzkycyziklistan 2017-04-24 16:04:52 -05:00
parent 664d4220a9
commit 017b75579f
10 changed files with 229 additions and 18 deletions

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace CircleScape {
class ActiveConnection : Websocket.Connection {
public ActiveConnection(TcpClient sock) : base(sock) { }
}
}

View file

@ -7,7 +7,7 @@
<ProjectGuid>{438DBAC1-BA37-40BB-9CCE-0FE1F23C6DC5}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>server</RootNamespace>
<RootNamespace>CircleScape</RootNamespace>
<AssemblyName>server</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
@ -57,8 +57,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Entrypoint.cs" />
<Compile Include="PoolManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="Websocket\Connection.cs" />
<Compile Include="Websocket\Frame.cs" />
<Compile Include="Websocket\Pool.cs" />
<Compile Include="Websocket\Server.cs" />
<Compile Include="Websocket\Stack.cs" />

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace CircleScape {
class PendingConnection : ActiveConnection {
public PendingConnection(TcpClient sock) : base(sock) { }
}
}

25
server/PoolManager.cs Normal file
View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CircleScape.Websocket;
namespace CircleScape {
static class PoolManager {
private static Pool<PendingConnection> PendingConnectionsPool;
private static Pool<ActiveConnection> ActiveConnectionsPool;
static PoolManager() {
PendingConnectionsPool = new Pool<PendingConnection> {
InitialCount = 1,
InitialSize = 10,
SizeGrowth = 10,
MaxSize = 50,
MaxCount = 5
};
ActiveConnectionsPool = new Pool<ActiveConnection>();
}
}
}

64
server/Utilities.cs Normal file
View file

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace CircleScape {
public static class StringExtensions {
public enum kHashReturnType {
RAW, HEX, BASE64
}
public static string Base64Encode(this string str, bool isUtf8 = true) {
var raw =
isUtf8 ? Encoding.UTF8.GetBytes(str)
: Encoding.ASCII.GetBytes(str);
return Convert.ToBase64String(raw);
}
public static string Base64Decode(this string str, bool isUtf8 = true) {
var raw = Convert.FromBase64String(str);
return isUtf8 ? Encoding.UTF8.GetString(raw)
: Encoding.ASCII.GetString(raw);
}
public static string SHA1(this string str, kHashReturnType type = kHashReturnType.RAW) {
using(var hasher = System.Security.Cryptography.SHA1.Create()) {
return ParseRawHash(
hasher.ComputeHash(Encoding.ASCII.GetBytes(str)),
type
);
}
}
public static string MD5(this string str, kHashReturnType type = kHashReturnType.RAW) {
using(var hasher = System.Security.Cryptography.MD5.Create()) {
return ParseRawHash(
hasher.ComputeHash(Encoding.ASCII.GetBytes(str)),
type
);
}
}
private static string ParseRawHash(byte[] hash, kHashReturnType type) {
var raw = Encoding.ASCII.GetString(hash);
switch(type) {
case kHashReturnType.BASE64:
return Base64Encode(raw, false);
case kHashReturnType.HEX:
return BitConverter.ToString(hash).Replace("-", "");
case kHashReturnType.RAW:
default:
return raw;
}
}
}
public static class NumericExtensions {
}
}

View file

@ -6,13 +6,40 @@ using System.Text;
using System.Threading.Tasks;
namespace CircleScape.Websocket {
class Connection {
abstract class Connection {
private TcpClient Socket;
private NetworkStream Stream;
public bool Disconnected { get; private set; } = false;
public string DisconnectReason { get; private set; } = null;
public bool Handshaked { get; private set; } = false;
private string RawClientHandshake = "";
private Dictionary<string, string> Headers =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public Connection(TcpClient sock) {
Socket = sock;
Socket.ReceiveTimeout = 1;
Stream = sock.GetStream();
}
public void Disconnect(string reason = null) {
Disconnected = true;
DisconnectReason = reason;
}
// called after the client successfully handshakes
public virtual void OnOpen() { }
// called when the thread manager iterates through
// the thread list and stops on this thread
public virtual void OnParse() { }
// called when data has been received
public virtual void OnReceive(byte[] data) { }
// called when the connection is disconnected
public virtual void OnClose() { }
}
}

11
server/Websocket/Frame.cs Normal file
View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CircleScape.Websocket {
class Frame {
}
}

View file

@ -5,11 +5,25 @@ using System.Text;
using System.Threading;
namespace CircleScape.Websocket {
class Pool {
private const int InitialCount = 3;
private const int InitialSize = 3;
private const int SizeGrowth = 1;
private const int MaxSize = 10;
class Pool<T> where T : Connection {
// number of threads that should be started when the pool is created
// these threads will run for as long as the pool exists
public int InitialCount { get; set; } = 3;
// amount of connections that should initially be allowed per thread
public int InitialSize { get; set; } = 3;
// amount of additional connections that each thread can handle after
// a new thread is created
public int SizeGrowth { get; set; } = 1;
// maximum amount of connections that a single thread will be assigned
public int MaxSize { get; set; } = 10;
// maximum number of threads that will be spawned
// 0 means no limit
public int MaxCount { get; set; } = 0;
// maximum number of connections in a thread that exceeds the calculated
// amount for the pool's thread count before the connection redistribution
// function is called
// 0 means never redistribute
public int Tolerance { get; set; } = 0;
private int _fullThreadCount;
private bool updateFullThreadCount = true;
@ -19,21 +33,47 @@ namespace CircleScape.Websocket {
private Dictionary<UInt64, Connection> Connections
= new Dictionary<UInt64, Connection>();
private List<ThreadContext> InvalidThreads
= new List<ThreadContext>();
private Mutex InvalidThreadsMutex = new Mutex();
public Pool() {
for(var i = 0; i < InitialCount; ++i)
CreateThread();
}
public bool AddConnection(Connection connection) {
foreach(var thread in Threads) {
if(InvalidThreads.Count > 0) {
foreach(var invalidThread in InvalidThreads)
Threads.RemoveAll(x => Object.ReferenceEquals(invalidThread, x));
updateFullThreadCount = true;
InvalidThreads.RemoveAll(x => true);
}
foreach(var thread in Threads) {
if(thread.Stack.Count < FullThreadSize) {
thread.Stack.AddClient(connection);
return true;
}
}
if(MaxCount == 0 || Threads.Count < MaxCount) {
CreateThread(connection);
return true;
}
return false;
}
public void InvalidateThread(Stack<T> stackRef) {
var ctx = Threads.FirstOrDefault(x => Object.ReferenceEquals(x.Stack, stackRef));
if(ctx != null)
InvalidThreads.Add(ctx);
}
private ThreadContext CreateThread(Connection initialConnection = null, bool runWithNoClients = false) {
var stack = new Stack(runWithNoClients, initialConnection);
var stack = new Stack<T>(this, runWithNoClients, initialConnection);
var ctx = new ThreadContext {
Stack = stack,
Thread = new Thread(new ThreadStart(stack.ManageStack))
@ -44,7 +84,7 @@ namespace CircleScape.Websocket {
return ctx;
}
private int FullThreadCount {
private int FullThreadSize {
get {
if(updateFullThreadCount) {
_fullThreadCount = Math.Min(
@ -59,7 +99,7 @@ namespace CircleScape.Websocket {
class ThreadContext {
public Thread Thread { get; set; }
public Stack Stack { get; set; }
public Stack<T> Stack { get; set; }
}
}
}

View file

@ -6,18 +6,22 @@ using System.Text;
using System.Threading;
namespace CircleScape.Websocket {
class Stack {
class Stack<T> where T : Connection {
private Pool<T> PoolRef = null;
private List<Connection> Clients = new List<Connection>();
private Mutex ClientsMutex = new Mutex();
private bool RunWithNoClients = false;
private bool Running = true;
private bool _finished = false;
public Stack(Connection initialConnection = null) {
public Stack(Pool<T> poolRef, Connection initialConnection = null) {
PoolRef = poolRef;
if(initialConnection != null)
Clients.Add(initialConnection);
}
public Stack(bool runWithNoClients, Connection initialConnection = null)
: this(initialConnection)
public Stack(Pool<T> poolRef, bool runWithNoClients, Connection initialConnection = null)
: this(poolRef, initialConnection)
{
RunWithNoClients = runWithNoClients;
}
@ -32,17 +36,30 @@ namespace CircleScape.Websocket {
return true;
}
public int ClientCount {
public int Count {
get {
return Clients.Count;
}
}
public void StopThread() {
Running = false;
}
public bool Finished { get; private set; }
// USED FOR THREADING -- DO NOT CALL
public void ManageStack() {
int clientCount = ClientCount;
while(Running && (Count > 0 || RunWithNoClients)) {
for(var i = Count - 1; i >= 0 && Running; ++i) {
PoolRef.OnConnectionParse(Clients[i]);
for(var i = 0; i < clientCount; ++i)
}
}
Finished = true;
PoolRef.InvalidateThread(this);
}
}
}