diff --git a/server/ActiveConnection.cs b/server/ActiveConnection.cs
new file mode 100644
index 0000000..0d114e4
--- /dev/null
+++ b/server/ActiveConnection.cs
@@ -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) { }
+ }
+}
diff --git a/server/CircleScape.csproj b/server/CircleScape.csproj
index 8477976..d1b2c21 100644
--- a/server/CircleScape.csproj
+++ b/server/CircleScape.csproj
@@ -7,7 +7,7 @@
{438DBAC1-BA37-40BB-9CCE-0FE1F23C6DC5}
Exe
Properties
- server
+ CircleScape
server
v4.5.2
512
@@ -57,8 +57,11 @@
+
+
+
diff --git a/server/server.sln b/server/CircleScape.sln
similarity index 100%
rename from server/server.sln
rename to server/CircleScape.sln
diff --git a/server/PendingConnection.cs b/server/PendingConnection.cs
new file mode 100644
index 0000000..cbac48e
--- /dev/null
+++ b/server/PendingConnection.cs
@@ -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) { }
+ }
+}
diff --git a/server/PoolManager.cs b/server/PoolManager.cs
new file mode 100644
index 0000000..dee7c56
--- /dev/null
+++ b/server/PoolManager.cs
@@ -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 PendingConnectionsPool;
+ private static Pool ActiveConnectionsPool;
+
+ static PoolManager() {
+ PendingConnectionsPool = new Pool {
+ InitialCount = 1,
+ InitialSize = 10,
+ SizeGrowth = 10,
+ MaxSize = 50,
+ MaxCount = 5
+ };
+
+ ActiveConnectionsPool = new Pool();
+ }
+ }
+}
diff --git a/server/Utilities.cs b/server/Utilities.cs
new file mode 100644
index 0000000..1d6e2cf
--- /dev/null
+++ b/server/Utilities.cs
@@ -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 {
+
+ }
+}
diff --git a/server/Websocket/Connection.cs b/server/Websocket/Connection.cs
index 5cae9c1..498d26c 100644
--- a/server/Websocket/Connection.cs
+++ b/server/Websocket/Connection.cs
@@ -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 Headers =
+ new Dictionary(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() { }
}
}
diff --git a/server/Websocket/Frame.cs b/server/Websocket/Frame.cs
new file mode 100644
index 0000000..7563ad3
--- /dev/null
+++ b/server/Websocket/Frame.cs
@@ -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 {
+
+ }
+}
diff --git a/server/Websocket/Pool.cs b/server/Websocket/Pool.cs
index 3d04699..e507a5b 100644
--- a/server/Websocket/Pool.cs
+++ b/server/Websocket/Pool.cs
@@ -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 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 Connections
= new Dictionary();
+ private List InvalidThreads
+ = new List();
+ 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 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(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 Stack { get; set; }
}
}
}
diff --git a/server/Websocket/Stack.cs b/server/Websocket/Stack.cs
index 968431e..06ebc1a 100644
--- a/server/Websocket/Stack.cs
+++ b/server/Websocket/Stack.cs
@@ -6,18 +6,22 @@ using System.Text;
using System.Threading;
namespace CircleScape.Websocket {
- class Stack {
+ class Stack where T : Connection {
+ private Pool PoolRef = null;
private List Clients = new List();
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 poolRef, Connection initialConnection = null) {
+ PoolRef = poolRef;
if(initialConnection != null)
Clients.Add(initialConnection);
}
- public Stack(bool runWithNoClients, Connection initialConnection = null)
- : this(initialConnection)
+ public Stack(Pool 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);
}
}
}