Skip to content

Commit

Permalink
Fully implemented large dataframes. We now send and receive data inte…
Browse files Browse the repository at this point in the history
…rnally with dataframes and expose simple byte and string send functions as well.
  • Loading branch information
DorianGray committed Dec 17, 2011
1 parent bfafbf0 commit 52d209c
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 208 deletions.
4 changes: 2 additions & 2 deletions Classes/Header.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ public Header(string data)
RequestPath = someFields["path"].Captures[0].Value.Trim();
Method = someFields["connect"].Captures[0].Value.Trim();

var version = 0;
int version;
Int32.TryParse(_fields["sec-websocket-version"], out version);

// ReSharper restore EmptyGeneralCatchClause

if (version >= 8)
Expand Down
33 changes: 26 additions & 7 deletions Classes/UserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,21 +216,38 @@ public void SetOnReceive(OnEventDelegate aDelegate)
/// <summary>
/// Sends the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="dataFrame">The data.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
public void Send(string data, bool close = false)
public void Send(DataFrame dataFrame, bool close = false)
{
Send(Encoding.GetBytes(data), close);
dataFrame.Wrap();
_context.Handler.Send(dataFrame, _context, close);
}

/// <summary>
/// Sends the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="aString">The data.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
public void Send(String aString, bool close = false)
{
DataFrame dataFrame = DataFrame.CreateInstance();
dataFrame.AppendStringToFrame(aString);
dataFrame.Wrap();
_context.Handler.Send(dataFrame, _context, close);
}

/// <summary>
/// Sends the specified data.
/// </summary>
/// <param name="someBytes">The data.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
public void Send(byte[] data, bool close = false)
public void Send(byte[] someBytes, bool close = false)
{
_context.Handler.Send(data, _context, close);
DataFrame dataFrame = DataFrame.CreateInstance();
dataFrame.AppendDataToFrame(someBytes);
dataFrame.Wrap();
_context.Handler.Send(dataFrame, _context, close);
}

/// <summary>
Expand All @@ -239,7 +256,9 @@ public void Send(byte[] data, bool close = false)
/// <param name="data">The data.</param>
public void SendRaw(byte[] data)
{
DefaultHandler.Instance.Send(data, _context);
DataFrame dataFrame = DataFrame.CreateInstance();
dataFrame.AppendDataToFrame(data);
DefaultHandler.Instance.Send(dataFrame, _context);
}
}

Expand Down
17 changes: 11 additions & 6 deletions Handlers/DefaultHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ You should have received a copy of the GNU Lesser General Public License

using System;
using System.Net.Sockets;
using System.Text;
using Alchemy.Server.Classes;
using Alchemy.Server.Handlers.WebSocket.hybi00;
using DataFrame = Alchemy.Server.Handlers.WebSocket.DataFrame;

namespace Alchemy.Server.Handlers
{
Expand Down Expand Up @@ -96,15 +98,15 @@ public void ProcessHeader(Context context)
{
case Protocol.WebSocketHybi00:
context.Handler = WebSocketHandler.Instance;
context.UserContext.DataFrame = new DataFrame();
context.UserContext.DataFrame = new WebSocket.hybi00.DataFrame();
break;
case Protocol.WebSocketHybi10:
context.Handler = WebSocket.hybi10.WebSocketHandler.Instance;
context.UserContext.DataFrame = new WebSocket.hybi10.DataFrame();
break;
case Protocol.FlashSocket:
context.Handler = WebSocketHandler.Instance;
context.UserContext.DataFrame = new DataFrame();
context.UserContext.DataFrame = new WebSocket.hybi00.DataFrame();
break;
default:
context.Header.Protocol = Protocol.None;
Expand All @@ -116,18 +118,20 @@ public void ProcessHeader(Context context)
}
else
{
context.UserContext.Send(Response.NotImplemented, true);
var dataFrame = (WebSocket.hybi00.DataFrame) context.UserContext.DataFrame.CreateInstance();
dataFrame.AppendDataToFrame(Encoding.UTF8.GetBytes(Response.NotImplemented));
context.UserContext.Send(dataFrame, true);
}
}
}

/// <summary>
/// Sends the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="dataFrame">The data.</param>
/// <param name="context">The user context.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
public override void Send(byte[] data, Context context, bool close = false)
public override void Send(DataFrame dataFrame, Context context, bool close = false)
{
AsyncCallback callback = EndSend;
if (close)
Expand All @@ -137,7 +141,8 @@ public override void Send(byte[] data, Context context, bool close = false)
context.SendReady.Wait();
try
{
context.Connection.Client.BeginSend(data, 0, data.Length, SocketFlags.None, callback, context);
context.Connection.Client.BeginSend(dataFrame.GetRaw(), SocketFlags.None, callback,
context);
}
catch
{
Expand Down
2 changes: 1 addition & 1 deletion Handlers/Handler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public abstract class Handler
protected static SemaphoreSlim CreateLock = new SemaphoreSlim(1);
public IWebSocketAuthentication Authentication;
public abstract void HandleRequest(Context request);
public abstract void Send(byte[] data, Context context, bool close = false);
public abstract void Send(DataFrame dataFrame, Context context, bool close = false);
public abstract void EndSend(IAsyncResult result);
public abstract void EndSendAndClose(IAsyncResult result);
}
Expand Down
50 changes: 26 additions & 24 deletions Handlers/WebSocket/DataFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,18 @@ public enum DataState
/// <summary>
/// The internal byte buffer used to store received data until the entire frame comes through.
/// </summary>
protected List<byte[]> RawFrame = new List<byte[]>();
protected List<ArraySegment<byte>> RawFrame = new List<ArraySegment<byte>>();

/// <summary>
/// Gets the current length of the received frame.
/// </summary>
public UInt64 Length
{

get
{
return RawFrame.Aggregate<byte[], ulong>(0, (current, data) => current + Convert.ToUInt64(data.Length));
return RawFrame.Aggregate<ArraySegment<byte>, ulong>(0,
(current, seg) =>
current + Convert.ToUInt64(seg.Count));
}
}

Expand All @@ -79,25 +80,19 @@ public DataState State
get { return InternalState; }
}

/// <summary>
/// Wraps the specified data.
/// Accepts a string, converts to bytes, sends to the real wrap function.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public byte[] Wrap(string data)
public abstract DataFrame CreateInstance();

public List<ArraySegment<byte>> GetRaw()
{
byte[] someBytes = Encoding.UTF8.GetBytes(data);
return Wrap(someBytes);
return RawFrame;
}

/// <summary>
/// Wraps the specified data in WebSocket Start/End Bytes.
/// Accepts a byte array.
/// Wraps the specified data.
/// Accepts a string, converts to bytes, sends to the real wrap function.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The data array wrapped in WebSocket DataFrame Start/End qualifiers.</returns>
public abstract byte[] Wrap(byte[] data);
/// <returns></returns>
public abstract void Wrap();

/// <summary>
/// Appends the specified data to the internal byte buffer.
Expand All @@ -114,9 +109,9 @@ public byte[] Wrap(string data)
public override string ToString()
{
var sb = new StringBuilder();
foreach(var data in RawFrame)
foreach (var data in RawFrame)
{
sb.Append(Encoding.UTF8.GetString(data));
sb.Append(Encoding.UTF8.GetString(data.Array));
}
return sb.ToString();
}
Expand All @@ -129,7 +124,7 @@ public override string ToString()
/// </returns>
public byte[] ToBytes()
{
return Encoding.UTF8.GetBytes(ToString());
return Encoding.UTF8.GetBytes(ToString());
}

/// <summary>
Expand All @@ -145,11 +140,18 @@ public void Clear()
/// Appends the data to frame. Manages recreating the byte array and such.
/// </summary>
/// <param name="someBytes">Some bytes.</param>
/// <param name="start">The start index.</param>
/// <param name="end">The end index.</param>
protected void AppendDataToFrame(byte[] someBytes)
public void AppendDataToFrame(byte[] someBytes)
{
RawFrame.Add(new ArraySegment<byte>(someBytes));
}

/// <summary>
/// Appends the data to frame. Manages recreating the byte array and such.
/// </summary>
/// <param name="aString">Some bytes.</param>
public void AppendStringToFrame(String aString)
{
RawFrame.Add(someBytes);
RawFrame.Add(new ArraySegment<byte>(Encoding.UTF8.GetBytes(aString)));
}
}
}
21 changes: 11 additions & 10 deletions Handlers/WebSocket/hybi00/DataFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,21 @@ public class DataFrame : WebSocket.DataFrame
public const byte StartByte = 0;
public const byte EndByte = 255;

public override WebSocket.DataFrame CreateInstance()
{
return new DataFrame();
}

/// <summary>
/// Wraps the specified data in WebSocket Start/End Bytes.
/// Accepts a byte array.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The data array wrapped in WebSocket DataFrame Start/End qualifiers.</returns>
public override byte[] Wrap(byte[] data)
public override void Wrap()
{
// wrap the array with the wrapper bytes
var wrappedBytes = new byte[data.Length + 2];
wrappedBytes[0] = StartByte;
wrappedBytes[wrappedBytes.Length - 1] = EndByte;
Array.Copy(data, 0, wrappedBytes, 1, data.Length);
return wrappedBytes;
var startBytes = new byte[1];
var endBytes = new byte[1];
RawFrame.Insert(0, new ArraySegment<byte>(startBytes));
RawFrame.Add(new ArraySegment<byte>(endBytes));
}

/// <summary>
Expand Down Expand Up @@ -80,7 +81,7 @@ public override void Append(byte[] data)
start = 0;
}

var temp = new byte[end-start];
var temp = new byte[end - start];
Array.Copy(data, start, temp, 0, end - start);
AppendDataToFrame(temp);
}
Expand Down
45 changes: 5 additions & 40 deletions Handlers/WebSocket/hybi00/WebSocketHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,36 +63,11 @@ public override void HandleRequest(Context context)
context.UserContext.DataFrame.Append(context.Buffer);
if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Complete)
{
switch (context.UserContext.DataFrame.Length)
{
case 1:
//Process Command
string command = context.UserContext.DataFrame.ToString();
if (command == context.Server.PingCommand)
{
SendPingResponse(context);
context.Pings++;
}
else
{
context.Pings = 0;
context.UserContext.OnReceive();
}
if ((context.Pings >= context.Server.MaxPingsInSequence) &&
(context.Server.MaxPingsInSequence != 0))
{
context.Dispose();
}
break;
default:
context.Pings = 0;
context.UserContext.OnReceive();
break;
}
context.UserContext.OnReceive();
}
else if (context.UserContext.DataFrame.State == WebSocket.DataFrame.DataState.Closed)
{
context.UserContext.Send(new byte[0], true);
context.UserContext.Send(new DataFrame(), true);
}
}
else
Expand Down Expand Up @@ -125,12 +100,11 @@ private static void Authenticate(Context context)
/// <summary>
/// Sends the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="dataFrame">The data.</param>
/// <param name="context">The user context.</param>
/// <param name="close">if set to <c>true</c> [close].</param>
public override void Send(byte[] data, Context context, bool close = false)
public override void Send(WebSocket.DataFrame dataFrame, Context context, bool close = false)
{
byte[] wrappedData = context.UserContext.DataFrame.Wrap(data);
AsyncCallback callback = EndSend;
if (close)
{
Expand All @@ -139,7 +113,7 @@ public override void Send(byte[] data, Context context, bool close = false)
context.SendReady.Wait();
try
{
context.Connection.Client.BeginSend(wrappedData, 0, wrappedData.Length, SocketFlags.None, callback,
context.Connection.Client.BeginSend(dataFrame.GetRaw(), SocketFlags.None, callback,
context);
}
catch
Expand Down Expand Up @@ -186,14 +160,5 @@ public override void EndSendAndClose(IAsyncResult result)
context.UserContext.OnSend();
context.Dispose();
}

/// <summary>
/// Sends the ping response.
/// </summary>
/// <param name="context">The user context.</param>
private static void SendPingResponse(Context context)
{
context.UserContext.Send(context.Server.PongCommand);
}
}
}
Loading

0 comments on commit 52d209c

Please sign in to comment.