using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text.RegularExpressions;
namespace Viz
{
///
/// Generic Async socket set up with resend built
///
public class ListenerSocket
{
///
/// Setting this flag to false will terminate the class.
///
private bool open;
///
/// The port that we are waiting on
///
private IPEndPoint end;
///
/// This object will generate the signon string when the session is opened
///
private static Transfer MainSender;
///
/// Provides the status of the socket across multiple instances.
///
protected static ManualResetEvent allDone = new ManualResetEvent(false);
///
/// This provides a simple way to set the port number and end point of the socket
///
/// Error handler
/// IP address object to connect with
public ListenerSocket(Transfer Sender, IPEndPoint localEndPoint)
{
end = localEndPoint;
MainSender = Sender;
open = true;
}
///
/// The method that will be called when the thread is started.
///
public void InstanceMethod()
{
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connection
try
{
listener.Bind(end);
listener.Listen(100);
while (open)
{
// Set the event to non-signaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
MainSender.RaiseError(String.Format("Waiting on {0} for connection", end), VizErrorLevel.note);
try
{
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
}
catch (SocketException ex)
{
VizErrorLevel level = VizErrorLevel.major;
String other = "Terminating listener";
if( ex.SocketErrorCode == SocketError.ConnectionReset )
{
level = VizErrorLevel.note;
other = String.Empty;
}
MainSender.RaiseError(String.Format("{0} Socket Exception: {1} {2}", other, ex.SocketErrorCode, ex.Message), level);
if (level != VizErrorLevel.note)
throw ex;
}
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
if( ! (e is SocketException) )
MainSender.RaiseError(String.Format("ListenerSocket: Stopping Listening! {0}", e.Message), VizErrorLevel.major, e);
}
finally
{
listener.Close();
}
}
private static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler;
//TODO : really tacky, socket is disposed!
try
{
handler = listener.EndAccept(ar);
}
catch (Exception )
{
return;
}
// Create the state object.
TransferState state = new TransferState(handler);
SignOn(handler);
handler.BeginReceive(state.buffer, 0, TransferState.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
private static void SignOn(Socket handler)
{
String temp = MainSender.ToString().Replace("\n", "\r\n"); // make look right in MS telnet
byte[] signOn = Encoding.ASCII.GetBytes(temp);
handler.Send(signOn);
}
private static void ReadCallback(IAsyncResult ar)
{
TransferState state = null;
bool CloseSocket = true; // If socket is reset then this is set to false as socket is gone!
try
{
String content = String.Empty;
bool completed = false;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
state = ar.AsyncState as TransferState;
Socket handler = state.workSocket;
Regex matchRegex = new Regex(@"group\s+(?[0-9]+).*message\s+(?[-0-9]+)\s+", RegexOptions.IgnoreCase | RegexOptions.Singleline);
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
Match m = matchRegex.Match(content);
if (m.Groups[1].Success && m.Groups[2].Success)
{
String Message = string.Format("\r\nSelected Group {0} and Message {1}\r\n",
m.Groups[1], m.Groups[2]);
handler.Send(Encoding.ASCII.GetBytes(Message));
String group = m.Groups[1].ToString();
int message = Convert.ToInt32(m.Groups[2].ToString());
if (MainSender.Id.Equals(group) && message < MainSender.messages.MessageCount)
{
MainSender.Resend(handler, message);
completed = true;
}
else
{
String Error = String.Format("Invalid request received from {0} Group '{1}' Message {2} \n{3}", ar.AsyncState, group, message, content);
MainSender.RaiseError(Error, VizErrorLevel.simple);
Send(handler, Error);
completed = true;
}
return;
}
if (content.IndexOf("help") > -1)
{
SignOn(handler);
state.sb.Replace("help", "");
}
if (content.IndexOf("") > -1)
{
// All the data has been read from the client. Display it
MainSender.RaiseError(String.Format("Read {0} bytes from socket. \n Data : {1}",
content.Length, content), VizErrorLevel.message);
// Echo the data back to the client.
Send(handler, content);
}
else if (!completed)
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, TransferState.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
catch (SocketException socketEx)
{
// 10035 == WSAEWOULDBLOCK
switch (socketEx.SocketErrorCode)
{
case SocketError.ConnectionReset: // forced closed by remote
CloseSocket = false;
Recorder.Message("Listener: Listener terminated unexpectedly!");
return;
case SocketError.ConnectionAborted: // forced closed by this program
case SocketError.Interrupted: // application exit
Recorder.Message(String.Format("Listener: Expected issue {0} {1}: {2}",
socketEx.NativeErrorCode, socketEx.SocketErrorCode, socketEx.Message));
return;
default:
String Error = String.Format("Socket exception {0} {1} received", socketEx.NativeErrorCode, socketEx.SocketErrorCode);
MainSender.RaiseError(Error, VizErrorLevel.major, socketEx);
return;
}
}
catch (Exception ex)
{
MainSender.RaiseError(String.Format("ListenerSocket Unknown exception {0}, contact support!", ex.Message), VizErrorLevel.major, ex);
}
finally
{
if (CloseSocket) // That is socket is not aborted
state.workSocket.Close();
}
}
private static void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
MainSender.RaiseError(String.Format("Sent {0} bytes to client.", bytesSent), VizErrorLevel.message);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
MainSender.RaiseError("Socket: SendCallback failed", VizErrorLevel.major, e);
}
}
///
/// Terminate the class closing all connections
///
public void Close()
{
open = false;
// Signal the listener thread to continue. Closes socket
allDone.Set();
allDone.Set();
}
}
}