Created
December 4, 2012 00:05
-
-
Save jhaygood86/4199234 to your computer and use it in GitHub Desktop.
Alchemy changes by Digital Generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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