Skip to content

Instantly share code, notes, and snippets.

@jhaygood86
Created December 4, 2012 00:05
Show Gist options
  • Save jhaygood86/4199234 to your computer and use it in GitHub Desktop.
Save jhaygood86/4199234 to your computer and use it in GitHub Desktop.
Alchemy changes by Digital Generation
Index: Z:/NotificationService/Alchemy/Handlers/Handler.cs
===================================================================
--- Z:/NotificationService/Alchemy/Handlers/Handler.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/Handlers/Handler.cs (revision 51941)
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Text;
using System.Threading;
@@ -16,21 +17,38 @@
{
private static Handler _instance;
- protected static SemaphoreSlim CreateLock = new SemaphoreSlim(1);
+ protected static object createLock = new object();
internal IAuthentication Authentication;
- protected Handler() {}
+ private Thread[] ProcessSendThreads = new Thread[Environment.ProcessorCount];
+
+ private ConcurrentQueue<HandlerMessage> MessageQueue { get; set; }
+
+ protected Handler() {
+
+ MessageQueue = new ConcurrentQueue<HandlerMessage>();
+
+ for (int i = 0; i < ProcessSendThreads.Length; i++)
+ {
+ ProcessSendThreads[i] = new Thread(ProcessSend);
+ ProcessSendThreads[i].Name = "Alchemy Send Handler Thread " + (i + 1);
+ ProcessSendThreads[i].Start();
+ }
+ }
+
public static Handler Instance
{
get
{
- if (_instance != null)
+ if (_instance == null)
{
- return _instance;
+ lock(createLock){
+ if(_instance == null){
+ _instance = new Handler();
+ }
+ }
}
- CreateLock.Wait();
- _instance = new Handler();
- CreateLock.Release();
+
return _instance;
}
}
@@ -73,12 +91,16 @@
switch (context.Header.Protocol)
{
case Protocol.WebSocketHybi00:
+ context.Handler.UnregisterContext(context);
context.Handler = WebSocket.hybi00.Handler.Instance;
context.UserContext.DataFrame = new WebSocket.hybi00.DataFrame();
+ context.Handler.RegisterContext(context);
break;
case Protocol.WebSocketRFC6455:
+ context.Handler.UnregisterContext(context);
context.Handler = WebSocket.rfc6455.Handler.Instance;
context.UserContext.DataFrame = new WebSocket.rfc6455.DataFrame();
+ context.Handler.RegisterContext(context);
break;
default:
context.Header.Protocol = Protocol.None;
@@ -95,6 +117,43 @@
}
}
+ private void ProcessSend()
+ {
+ while (true)
+ {
+ while (MessageQueue.IsEmpty)
+ {
+ Thread.Sleep(10);
+ }
+
+ HandlerMessage message;
+
+ if (!MessageQueue.TryDequeue(out message))
+ {
+ continue;
+ }
+
+ Send(message);
+ }
+ }
+
+ private void Send(HandlerMessage message)
+ {
+ message.Context.SendEventArgs.UserToken = message;
+ message.Context.SendReady.Wait();
+
+ try
+ {
+ List<ArraySegment<byte>> data = message.IsRaw ? message.DataFrame.AsRaw() : message.DataFrame.AsFrame();
+ message.Context.SendEventArgs.BufferList = data;
+ message.Context.Connection.Client.SendAsync(message.Context.SendEventArgs);
+ }
+ catch
+ {
+ message.Context.Disconnect();
+ }
+ }
+
/// <summary>
/// Sends the specified data.
/// </summary>
@@ -106,54 +165,46 @@
{
if (context.Connected)
{
- AsyncCallback callback = EndSend;
- if (close)
- {
- callback = EndSendAndClose;
- }
- context.SendReady.Wait();
- try
- {
- List<ArraySegment<byte>> data = raw ? dataFrame.AsRaw() : dataFrame.AsFrame();
- context.Connection.Client.BeginSend(data, SocketFlags.None,
- callback,
- context);
- }
- catch
- {
- context.Disconnect();
- }
+ HandlerMessage message = new HandlerMessage { DataFrame = dataFrame, Context = context, IsRaw = raw, DoClose = close };
+ MessageQueue.Enqueue(message);
}
}
- /// <summary>
- /// Ends the send.
- /// </summary>
- /// <param name="result">The Async result.</param>
- public void EndSend(IAsyncResult result)
+ void SendEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
- var context = (Context) result.AsyncState;
- try
+ HandlerMessage message = (HandlerMessage)e.UserToken;
+
+ if (e.SocketError != SocketError.Success)
{
- context.Connection.Client.EndSend(result);
- context.SendReady.Release();
+ message.Context.Disconnect();
+ return;
}
- catch
+
+ message.Context.SendReady.Release();
+ message.Context.UserContext.OnSend();
+
+ if (message.DoClose)
{
- context.Disconnect();
+ message.Context.Disconnect();
}
- context.UserContext.OnSend();
}
- /// <summary>
- /// Ends the send and closes the connection.
- /// </summary>
- /// <param name="result">The Async result.</param>
- public void EndSendAndClose(IAsyncResult result)
+ public void RegisterContext(Context context)
{
- var context = (Context) result.AsyncState;
- EndSend(result);
- context.Disconnect();
+ context.SendEventArgs.Completed += SendEventArgs_Completed;
}
+
+ public void UnregisterContext(Context context)
+ {
+ context.SendEventArgs.Completed -= SendEventArgs_Completed;
+ }
+
+ private class HandlerMessage
+ {
+ public DataFrame DataFrame { get; set;}
+ public Context Context { get; set;}
+ public Boolean IsRaw { get; set;}
+ public Boolean DoClose { get; set;}
+ }
}
}
\ No newline at end of file
Index: Z:/NotificationService/Alchemy/Handlers/WebSocket/hybi00/Handler.cs
===================================================================
--- Z:/NotificationService/Alchemy/Handlers/WebSocket/hybi00/Handler.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/Handlers/WebSocket/hybi00/Handler.cs (revision 51941)
@@ -10,21 +10,25 @@
private Handler()
{
Authentication = new Authentication();
+ }
+
+ public new static Handler Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ lock (createLock)
+ {
+ if (_instance == null)
+ {
+ _instance = new Handler();
+ }
+ }
+ }
+
+ return _instance;
+ }
}
-
- public new static Handler Instance
- {
- get
- {
- if (_instance != null)
- {
- return _instance;
- }
- CreateLock.Wait();
- _instance = new Handler();
- CreateLock.Release();
- return _instance;
- }
- }
}
}
\ No newline at end of file
Index: Z:/NotificationService/Alchemy/Handlers/WebSocket/rfc6455/Handler.cs
===================================================================
--- Z:/NotificationService/Alchemy/Handlers/WebSocket/rfc6455/Handler.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/Handlers/WebSocket/rfc6455/Handler.cs (revision 51941)
@@ -16,13 +16,17 @@
{
get
{
- if (_instance != null)
+ if (_instance == null)
{
- return _instance;
+ lock (createLock)
+ {
+ if (_instance == null)
+ {
+ _instance = new Handler();
+ }
+ }
}
- CreateLock.Wait();
- _instance = new Handler();
- CreateLock.Release();
+
return _instance;
}
}
Index: Z:/NotificationService/Alchemy/Alchemy.csproj
===================================================================
--- Z:/NotificationService/Alchemy/Alchemy.csproj (revision 51940)
+++ Z:/NotificationService/Alchemy/Alchemy.csproj (revision 51941)
@@ -74,6 +74,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="log4net">
+ <HintPath>..\packages\log4net.2.0.0\lib\net40-full\log4net.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Web" />
<Reference Include="System.XML" />
@@ -108,6 +111,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SubType>Designer</SubType>
</None>
+ <None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Index: Z:/NotificationService/Alchemy/WebSocketServer.cs
===================================================================
--- Z:/NotificationService/Alchemy/WebSocketServer.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/WebSocketServer.cs (revision 51941)
@@ -3,6 +3,11 @@
using System.Net.Sockets;
using Alchemy.Classes;
using Alchemy.Handlers;
+using System.Threading;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using log4net;
+using System.Reflection;
namespace Alchemy
{
@@ -13,6 +18,8 @@
/// </summary>
public class WebSocketServer : TcpServer, IDisposable
{
+ private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
/// <summary>
/// This is the Flash Access Policy Server. It allows us to facilitate flash socket connections much more quickly in most cases.
/// Don't mess with it through here. It's only public so we can access it later from all the IOCPs.
@@ -53,6 +60,94 @@
private string _destination = String.Empty;
private string _origin = String.Empty;
+ private static Thread[] ClientThreads = new Thread[Environment.ProcessorCount];
+ private static Thread CleanupThread;
+
+ private static ConcurrentQueue<Context> ContextQueue { get; set; }
+ private static Dictionary<Context, WebSocketServer> ContextMapping { get; set; }
+
+ private static List<Context> CurrentConnections { get; set; }
+
+ static WebSocketServer()
+ {
+ ContextQueue = new ConcurrentQueue<Context>();
+ ContextMapping = new Dictionary<Context, WebSocketServer>();
+ CurrentConnections = new List<Context>();
+
+ CleanupThread = new Thread(HandleContextCleanupThread);
+ CleanupThread.Name = "WebSocketServer Cleanup Thread";
+ CleanupThread.Start();
+
+ for(int i = 0; i < ClientThreads.Length; i++){
+ ClientThreads[i] = new Thread(HandleClientThread);
+ ClientThreads[i].Name = "WebSocketServer Client Thread #" + (i + 1);
+ ClientThreads[i].Start();
+ }
+ }
+
+ private static void HandleClientThread()
+ {
+ while (true)
+ {
+ Context context;
+
+ while (ContextQueue.Count == 0)
+ {
+ Thread.Sleep(10);
+ }
+
+ if (!ContextQueue.TryDequeue(out context))
+ {
+ continue;
+ }
+
+ lock (ContextMapping)
+ {
+ WebSocketServer client = ContextMapping[context];
+ client.SetupContext(context);
+ }
+
+ lock(CurrentConnections){
+ CurrentConnections.Add(context);
+ }
+ }
+ }
+
+ private static void HandleContextCleanupThread()
+ {
+ while (true)
+ {
+ Thread.Sleep(100);
+
+ List<Context> currentConnections = new List<Context>();
+
+ lock (CurrentConnections)
+ {
+ currentConnections.AddRange(CurrentConnections);
+ }
+
+ foreach (var connection in currentConnections)
+ {
+ if (!connection.Connected)
+ {
+ lock (CurrentConnections)
+ {
+ CurrentConnections.Remove(connection);
+ }
+
+ lock (ContextMapping)
+ {
+ ContextMapping.Remove(connection);
+ }
+
+ connection.Handler.UnregisterContext(connection);
+
+ connection.Dispose();
+ }
+ }
+ }
+ }
+
/// <summary>
/// Initializes a new instance of the <see cref="WebSocketServer"/> class.
/// </summary>
@@ -127,59 +222,81 @@
protected override void OnRunClient(object data)
{
var connection = (TcpClient)data;
- using (var context = new Context(this, connection))
+ var context = new Context(this, connection);
+
+ context.UserContext.ClientAddress = context.Connection.Client.RemoteEndPoint;
+ context.UserContext.SetOnConnect(OnConnect);
+ context.UserContext.SetOnConnected(OnConnected);
+ context.UserContext.SetOnDisconnect(OnDisconnect);
+ context.UserContext.SetOnSend(OnSend);
+ context.UserContext.SetOnReceive(OnReceive);
+ context.BufferSize = BufferSize;
+ context.UserContext.OnConnect();
+
+ if (context.Connected)
{
- context.UserContext.ClientAddress = context.Connection.Client.RemoteEndPoint;
- context.UserContext.SetOnConnect(OnConnect);
- context.UserContext.SetOnConnected(OnConnected);
- context.UserContext.SetOnDisconnect(OnDisconnect);
- context.UserContext.SetOnSend(OnSend);
- context.UserContext.SetOnReceive(OnReceive);
- context.BufferSize = BufferSize;
- context.UserContext.OnConnect();
- while (context.Connected)
+ lock (ContextMapping)
{
- if (context.ReceiveReady.Wait(TimeOut))
+ ContextMapping[context] = this;
+ }
+
+ ContextQueue.Enqueue(context);
+ }
+ }
+
+ private void SetupContext(Context _context)
+ {
+ _context.ReceiveEventArgs.UserToken = _context;
+ _context.ReceiveEventArgs.Completed += ReceiveEventArgs_Completed;
+ _context.ReceiveEventArgs.SetBuffer(_context.Buffer, 0, _context.Buffer.Length);
+
+ StartReceive(_context);
+ }
+
+ private void StartReceive(Context _context)
+ {
+ if (_context.ReceiveReady.Wait(TimeOut))
+ {
+ try
+ {
+ if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
{
- try
- {
- context.Connection.Client.BeginReceive(context.Buffer, 0, context.Buffer.Length,
- SocketFlags.None, DoReceive, context);
- }
- catch (SocketException)
- {
- break;
- }
+ ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
}
- else
- {
- break;
- }
}
+ catch (SocketException ex)
+ {
+ logger.Error("SocketException in ReceieveAsync", ex);
+ _context.Disconnect();
+ }
}
+ else
+ {
+ logger.Error("Timeout waiting for ReceiveReady");
+ _context.Disconnect();
+ }
}
- /// <summary>
- /// The root receive event for each client. Executes in it's own thread.
- /// </summary>
- /// <param name="result">The Async result.</param>
- private void DoReceive(IAsyncResult result)
+ void ReceiveEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
- var context = (Context) result.AsyncState;
+ var context = (Context)e.UserToken;
context.Reset();
- try
+
+ if (e.SocketError != SocketError.Success)
{
- context.ReceivedByteCount = context.Connection.Client.EndReceive(result);
+ logger.Error("Socket Error: " + e.SocketError.ToString());
+ context.ReceivedByteCount = 0;
}
- catch
+ else
{
- context.ReceivedByteCount = 0;
+ context.ReceivedByteCount = e.BytesTransferred;
}
if (context.ReceivedByteCount > 0)
{
context.Handler.HandleRequest(context);
context.ReceiveReady.Release();
+ StartReceive(context);
}
else
{
Index: Z:/NotificationService/Alchemy/packages.config
===================================================================
--- Z:/NotificationService/Alchemy/packages.config (revision 0)
+++ Z:/NotificationService/Alchemy/packages.config (revision 51941)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="log4net" version="2.0.0" targetFramework="net40" />
+</packages>
\ No newline at end of file
Index: Z:/NotificationService/Alchemy/WebSocketClient.cs
===================================================================
--- Z:/NotificationService/Alchemy/WebSocketClient.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/WebSocketClient.cs (revision 51941)
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
@@ -33,6 +34,10 @@
private readonly int _port;
private readonly string _host;
+ private static Thread[] ClientThreads = new Thread[Environment.ProcessorCount];
+ private static Queue<Context> NewClients { get; set; }
+ private static Dictionary<Context, WebSocketClient> ContextMapping { get; set; }
+
public enum ReadyStates
{
CONNECTING,
@@ -49,6 +54,46 @@
}
}
+ static WebSocketClient()
+ {
+ NewClients = new Queue<Context>();
+ ContextMapping = new Dictionary<Context, WebSocketClient>();
+
+ for(int i = 0; i < ClientThreads.Length; i++){
+ ClientThreads[i] = new Thread(HandleClientThread);
+ ClientThreads[i].Start();
+ }
+ }
+
+ private static void HandleClientThread()
+ {
+ while (true)
+ {
+ Context context = null;
+
+ while (NewClients.Count == 0)
+ {
+ Thread.Sleep(10);
+ }
+
+ lock (NewClients)
+ {
+ if (NewClients.Count == 0)
+ {
+ continue;
+ }
+
+ context = NewClients.Dequeue();
+ }
+
+ lock (ContextMapping)
+ {
+ WebSocketClient client = ContextMapping[context];
+ client.SetupContext(context);
+ }
+ }
+ }
+
public WebSocketClient(string path)
{
var r = new Regex("^(wss?)://(.*)\\:([0-9]*)/(.*)$");
@@ -97,49 +142,60 @@
{
_client.EndConnect(result);
}
- catch (Exception)
+ catch (Exception ex)
{
Disconnect();
connectError = true;
}
- using (_context = new Context(null, _client))
+ _context = new Context(null, _client);
+ _context.BufferSize = 512;
+ _context.UserContext.DataFrame = new DataFrame();
+ _context.UserContext.SetOnConnect(OnConnect);
+ _context.UserContext.SetOnConnected(OnConnected);
+ _context.UserContext.SetOnDisconnect(OnDisconnect);
+ _context.UserContext.SetOnSend(OnSend);
+ _context.UserContext.SetOnReceive(OnReceive);
+ _context.UserContext.OnConnect();
+
+ if (connectError)
{
- _context.BufferSize = 512;
- _context.UserContext.DataFrame = new DataFrame();
- _context.UserContext.SetOnConnect(OnConnect);
- _context.UserContext.SetOnConnected(OnConnected);
- _context.UserContext.SetOnDisconnect(OnDisconnect);
- _context.UserContext.SetOnSend(OnSend);
- _context.UserContext.SetOnReceive(OnReceive);
- _context.UserContext.OnConnect();
+ _context.UserContext.OnDisconnect();
+ return;
+ }
- if (connectError)
+ lock (ContextMapping)
+ {
+ ContextMapping[_context] = this;
+ }
+
+ lock (NewClients)
+ {
+ NewClients.Enqueue(_context);
+ }
+ }
+
+ private void SetupContext(Context context)
+ {
+ _context.ReceiveEventArgs.UserToken = _context;
+ _context.ReceiveEventArgs.Completed += ReceiveEventArgs_Completed;
+ _context.ReceiveEventArgs.SetBuffer(_context.Buffer, 0, _context.Buffer.Length);
+
+
+ if (_context.Connection != null && _context.Connection.Connected)
+ {
+ _context.ReceiveReady.Wait();
+
+ if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
{
- _context.UserContext.OnDisconnect();
+ ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
}
- while (_context.Connection != null && _context.Connection.Connected)
+ if (!IsAuthenticated)
{
- _context.ReceiveReady.Wait();
-
- try
- {
- _context.Connection.Client.BeginReceive(_context.Buffer, 0, _context.Buffer.Length, SocketFlags.None, DoReceive, _context);
- }
- catch (Exception)
- {
- break;
- }
-
- if (!IsAuthenticated)
- {
- Authenticate();
- }
+ Authenticate();
}
}
-
- Disconnect();
}
private void Authenticate()
@@ -211,6 +267,38 @@
}
}
+ void ReceiveEventArgs_Completed(object sender, SocketAsyncEventArgs e)
+ {
+ var context = (Context)e.UserToken;
+ context.Reset();
+
+ if (e.SocketError != SocketError.Success)
+ {
+ context.ReceivedByteCount = 0;
+ }
+ else
+ {
+ context.ReceivedByteCount = e.BytesTransferred;
+ }
+
+ if (context.ReceivedByteCount > 0)
+ {
+ ReceiveData(context);
+ context.ReceiveReady.Release();
+ }
+ else
+ {
+ context.Disconnect();
+ }
+
+ _context.ReceiveReady.Wait();
+
+ if (!_context.Connection.Client.ReceiveAsync(_context.ReceiveEventArgs))
+ {
+ ReceiveEventArgs_Completed(_context.Connection.Client, _context.ReceiveEventArgs);
+ }
+ }
+
private void DoReceive(IAsyncResult result)
{
var context = (Context) result.AsyncState;
Index: Z:/NotificationService/Alchemy/Classes/Context.cs
===================================================================
--- Z:/NotificationService/Alchemy/Classes/Context.cs (revision 51940)
+++ Z:/NotificationService/Alchemy/Classes/Context.cs (revision 51941)
@@ -1,141 +1,155 @@
-using System;
-using System.Net.Sockets;
-using System.Threading;
-using Alchemy.Handlers;
-using Alchemy.Handlers.WebSocket;
-
-namespace Alchemy.Classes
-{
- /// <summary>
- /// This class contains the required data for each connection to the server.
- /// </summary>
- public class Context : IDisposable
- {
- /// <summary>
- /// The exported version of this context.
- /// </summary>
- public readonly UserContext UserContext;
-
- /// <summary>
- /// The buffer used for accepting raw data from the socket.
- /// </summary>
- public byte[] Buffer;
-
- /// <summary>
- /// Whether or not the TCPClient is still connected.
- /// </summary>
- public bool Connected = true;
-
- /// <summary>
- /// The raw client connection.
- /// </summary>
- public TcpClient Connection;
-
- /// <summary>
- /// The current connection handler.
- /// </summary>
- public Handler Handler = Handler.Instance;
-
- /// <summary>
- /// The Header
- /// </summary>
- public Header Header;
-
- /// <summary>
- /// Whether or not this client has passed all the setup routines for the current handler(authentication, etc)
- /// </summary>
- public Boolean IsSetup;
-
- /// <summary>
- /// The max frame that we will accept from the client
- /// </summary>
- public UInt64 MaxFrameSize = 102400; //100kb
-
- /// <summary>
- /// Semaphores that limit sends and receives to 1 and a time.
- /// </summary>
- public SemaphoreSlim ReceiveReady = new SemaphoreSlim(1);
-
- /// <summary>
- /// How many bytes we received this tick.
- /// </summary>
- public int ReceivedByteCount;
-
- public SemaphoreSlim SendReady = new SemaphoreSlim(1);
-
- /// <summary>
- /// A link to the server listener instance this client is currently hosted on.
- /// </summary>
- public WebSocketServer Server;
-
- private int _bufferSize = 512;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Context"/> class.
- /// </summary>
- public Context(WebSocketServer server, TcpClient connection)
- {
- Server = server;
- Connection = connection;
- Buffer = new byte[_bufferSize];
- UserContext = new UserContext(this);
-
- if(connection != null){
- UserContext.ClientAddress = connection.Client.RemoteEndPoint;
- }
- }
-
- /// <summary>
- /// Gets or sets the size of the buffer.
- /// </summary>
- /// <value>
- /// The size of the buffer.
- /// </value>
- public int BufferSize
- {
- get { return _bufferSize; }
- set
- {
- _bufferSize = value;
- Buffer = new byte[_bufferSize];
- }
- }
-
- #region IDisposable Members
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Connected = false;
- UserContext.OnDisconnect();
- }
-
- #endregion
-
- /// <summary>
- /// Disconnects the client
- /// </summary>
- public void Disconnect()
- {
- Connected = false;
- }
-
- /// <summary>
- /// Resets this instance.
- /// Clears the dataframe if necessary. Resets Received byte count.
- /// </summary>
- public void Reset()
- {
- if (UserContext.DataFrame != null)
- {
- if (UserContext.DataFrame.State == DataFrame.DataState.Complete)
- {
- UserContext.DataFrame.Reset();
- }
- }
- ReceivedByteCount = 0;
- }
- }
+using System;
+using System.Net.Sockets;
+using System.Threading;
+using Alchemy.Handlers;
+using Alchemy.Handlers.WebSocket;
+using log4net;
+using System.Reflection;
+
+namespace Alchemy.Classes
+{
+ /// <summary>
+ /// This class contains the required data for each connection to the server.
+ /// </summary>
+ public class Context : IDisposable
+ {
+ private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ /// <summary>
+ /// The exported version of this context.
+ /// </summary>
+ public readonly UserContext UserContext;
+
+ /// <summary>
+ /// The buffer used for accepting raw data from the socket.
+ /// </summary>
+ public byte[] Buffer;
+
+ /// <summary>
+ /// Whether or not the TCPClient is still connected.
+ /// </summary>
+ public bool Connected = true;
+
+ /// <summary>
+ /// The raw client connection.
+ /// </summary>
+ public TcpClient Connection;
+
+ /// <summary>
+ /// The current connection handler.
+ /// </summary>
+ public Handler Handler = Handler.Instance;
+
+ /// <summary>
+ /// The Header
+ /// </summary>
+ public Header Header;
+
+ /// <summary>
+ /// Whether or not this client has passed all the setup routines for the current handler(authentication, etc)
+ /// </summary>
+ public Boolean IsSetup;
+
+ /// <summary>
+ /// The max frame that we will accept from the client
+ /// </summary>
+ public UInt64 MaxFrameSize = 102400; //100kb
+
+ /// <summary>
+ /// Semaphores that limit sends and receives to 1 and a time.
+ /// </summary>
+ public SemaphoreSlim ReceiveReady = new SemaphoreSlim(1);
+
+ /// <summary>
+ /// How many bytes we received this tick.
+ /// </summary>
+ public int ReceivedByteCount;
+
+ public SemaphoreSlim SendReady = new SemaphoreSlim(1);
+
+ /// <summary>
+ /// A link to the server listener instance this client is currently hosted on.
+ /// </summary>
+ public WebSocketServer Server;
+
+ private int _bufferSize = 512;
+
+
+ public SocketAsyncEventArgs ReceiveEventArgs { get; set; }
+ public SocketAsyncEventArgs SendEventArgs { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Context"/> class.
+ /// </summary>
+ public Context(WebSocketServer server, TcpClient connection)
+ {
+ Server = server;
+ Connection = connection;
+ Buffer = new byte[_bufferSize];
+ UserContext = new UserContext(this);
+
+ ReceiveEventArgs = new SocketAsyncEventArgs();
+ SendEventArgs = new SocketAsyncEventArgs();
+
+ Handler.RegisterContext(this);
+
+ if(connection != null){
+ UserContext.ClientAddress = connection.Client.RemoteEndPoint;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the buffer.
+ /// </summary>
+ /// <value>
+ /// The size of the buffer.
+ /// </value>
+ public int BufferSize
+ {
+ get { return _bufferSize; }
+ set
+ {
+ _bufferSize = value;
+ Buffer = new byte[_bufferSize];
+ }
+ }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Connected = false;
+ UserContext.OnDisconnect();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Disconnects the client
+ /// </summary>
+ public void Disconnect()
+ {
+ logger.Debug("Disconnected in " + Environment.StackTrace);
+ Connected = false;
+ }
+
+ /// <summary>
+ /// Resets this instance.
+ /// Clears the dataframe if necessary. Resets Received byte count.
+ /// </summary>
+ public void Reset()
+ {
+ if (UserContext.DataFrame != null)
+ {
+ if (UserContext.DataFrame.State == DataFrame.DataState.Complete)
+ {
+ UserContext.DataFrame.Reset();
+ }
+ }
+ ReceivedByteCount = 0;
+ }
+ }
}
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment