|
using System;
using System.Text;
using System.Net.Sockets;
using System.IO;
namespace WebSocketServer
{
public class DataReceivedEventArgs
{
public int Size { get; private set; }
public string Data { get; private set; }
public DataReceivedEventArgs(int size, string data)
{
Size = size;
Data = data;
}
}
public delegate void DataReceivedEventHandler(WebSocketConnection sender, DataReceivedEventArgs e);
public delegate void WebSocketDisconnectedEventHandler(WebSocketConnection sender, EventArgs e);
public class WebSocketConnection : IDisposable
{
#region Private members
private byte[] dataBuffer; // buffer to hold the data we are reading
bool readingData; // are we in the proccess of reading data or not
private StringBuilder dataString; // holds the currently accumulated data
private enum WrapperBytes : byte { Start = 0, End = 255 }; // data passed between client and server are wrapped in start and end bytes according to the protocol (0x00, 0xFF)
#endregion
/// <summary>
/// An event that is triggered whenever the connection has read some data from the client
/// </summary>
public event DataReceivedEventHandler DataReceived;
public event WebSocketDisconnectedEventHandler Disconnected;
/// <summary>
/// Guid for the connection - thouhgt it might be usable in some way
/// </summary>
public System.Guid GUID { get; private set; }
/// <summary>
/// Gets the socket used for the connection
/// </summary>
public Socket ConnectionSocket { get; private set; }
#region Constructors
/// <summary>
/// constructor
/// </summary>
/// <param name="connection">The socket on which to esablish the connection</param>
/// <param name="webSocketOrigin">The origin from which the server is willing to accept connections, usually this is your web server. For example: http://localhost:8080.</param>
/// <param name="webSocketLocation">The location of the web socket server (the server on which this code is running). For example: ws://localhost:8181/service. The '/service'-part is important it could be '/somethingelse' but it needs to be there.</param>
public WebSocketConnection(Socket socket)
: this(socket, 255)
{
}
/// <summary>
/// constructor
/// </summary>
/// <param name="connection">The socket on which to esablish the connection</param>
/// <param name="webSocketOrigin">The origin from which the server is willing to accept connections, usually this is your web server. For example: http://localhost:8080.</param>
/// <param name="webSocketLocation">The location of the web socket server (the server on which this code is running). For example: ws://localhost:8181/service. The '/service'-part is important it could be '/somethingelse' but it needs to be there.</param>
/// <param name="bufferSize">The size of the buffer used to receive data</param>
public WebSocketConnection(Socket socket, int bufferSize)
{
ConnectionSocket = socket;
dataBuffer = new byte[bufferSize];
dataString = new StringBuilder();
GUID = System.Guid.NewGuid();
Listen();
}
#endregion
/// <summary>
/// Invoke the DataReceived event, called whenever the client has finished sending data.
/// </summary>
protected virtual void OnDataReceived(DataReceivedEventArgs e)
{
if (DataReceived != null)
DataReceived(this, e);
}
/// <summary>
/// Listens for incomming data
/// </summary>
private void Listen()
{
ConnectionSocket.BeginReceive(dataBuffer, 0, dataBuffer.Length, 0, Read, null);
}
/// <summary>
/// Send a string to the client
/// </summary>
/// <param name="str">the string to send to the client</param>
public void Send(string str)
{
if (ConnectionSocket.Connected)
{
try
{
// start with a 0x00
ConnectionSocket.Send(new byte[] { (byte)WrapperBytes.Start }, 1, 0);
// send the string
ConnectionSocket.Send(Encoding.UTF8.GetBytes(str));
/*
writer.Write(str);
writer.Flush();*/
// end with a 0xFF
ConnectionSocket.Send(new byte[] { (byte)WrapperBytes.End }, 1, 0);
}
catch
{
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}
}
}
/// <summary>
/// reads the incomming data and triggers the DataReceived event when done
/// </summary>
private void Read(IAsyncResult ar)
{
int sizeOfReceivedData = ConnectionSocket.EndReceive(ar);
if (sizeOfReceivedData > 0)
{
int start = 0, end = dataBuffer.Length - 1;
// if we are not already reading something, look for the start byte as specified in the protocol
if (!readingData)
{
for (start = 0; start < dataBuffer.Length - 1; start++)
{
if (dataBuffer[start] == (byte)WrapperBytes.Start)
{
readingData = true; // we found the begining and can now start reading
start++; // we dont need the start byte. Incrementing the start counter will walk us past it
break;
}
}
} // no else here, the value of readingData might have changed
// if a begining was found in the buffer, or if we are continuing from another buffer
if (readingData)
{
bool endIsInThisBuffer = false;
// look for the end byte in the received data
for (end = start; end < sizeOfReceivedData; end++)
{
byte currentByte = dataBuffer[end];
if (dataBuffer[end] == (byte)WrapperBytes.End)
{
endIsInThisBuffer = true; // we found the ending byte
break;
}
}
// the end is in this buffer, which means that we are done reading
if (endIsInThisBuffer)
{
// we are no longer reading data
readingData = false;
// put the data into the string builder
dataString.Append(Encoding.UTF8.GetString(dataBuffer, start, end - start));
// trigger the event
int size = Encoding.UTF8.GetBytes(dataString.ToString().ToCharArray()).Length;
OnDataReceived(new DataReceivedEventArgs(size, dataString.ToString()));
dataString = null;
dataString = new StringBuilder();
}
else // if the end is not in this buffer then put everyting from start to the end of the buffer into the datastring and keep on reading
{
dataString.Append(Encoding.UTF8.GetString(dataBuffer, start, end - start));
}
}
// continue listening for more data
Listen();
}
else // the socket is closed
{
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}
}
#region cleanup
/// <summary>
/// Closes the socket
/// </summary>
public void Close()
{
ConnectionSocket.Close();
}
/// <summary>
/// Closes the socket
/// </summary>
public void Dispose()
{
Close();
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.