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(); } } }