/**////<summary> /// Author: Asad Aziz /// Description: State object for reading client data asynchronously. ///</summary> publicclass StateObject ...{ publicbool connected =false; // ID received flag public Socket workSocket =null; // Client socket. public Socket partnerSocket =null; // Partner socket. publicconstint BufferSize =1024; // Size of receive buffer. publicbyte[] buffer =newbyte[BufferSize];// Receive buffer. public StringBuilder sb =new StringBuilder();//Received data String. publicstring id = String.Empty; // Host or conversation ID public DateTime TimeStamp; } /**////<summary> /// Author: Asad Aziz /// Description: Server is the class to control sockets ///</summary> publicclass Server ...{ protectedint portNumber; protectedint maxSockets; protectedint sockCount =0; privateint convID =0; private Timer lostTimer; privateconstint numThreads =1; privateconstint timerTimeout =300000; privateconstint timeoutMinutes =3; privatebool ShuttingDown =false; protectedstring title; protected Hashtable connectedHT =new Hashtable(); protected ArrayList connectedSocks; //Thread signal. private ManualResetEvent allDone =new ManualResetEvent(false); private Thread[] serverThread =new Thread[numThreads]; private AutoResetEvent[] threadEnd =new AutoResetEvent[numThreads]; public Server(int port, string title, string attr) ...{ this.portNumber = port; this.title = title; this.maxSockets =10000; connectedSocks =new ArrayList(this.maxSockets); } /**////<summary> /// Description: Start the threads to listen to the port and process /// messages. ///</summary> publicvoid Start() ...{ // Clear the thread end events for (int lcv =0; lcv < numThreads; lcv++) threadEnd[lcv] =new AutoResetEvent(false); ThreadStart threadStart1 =new ThreadStart(StartListening); serverThread[0] =new Thread(threadStart1); serverThread[0].IsBackground =true; serverThread[0].Start(); // Create the delegate that invokes methods for the timer. TimerCallback timerDelegate =new TimerCallback(this.CheckSockets); //Create a timer that waits one minute, then invokes every 5 minutes. lostTimer =new Timer(timerDelegate, null, Server.timerTimeout, Server.timerTimeout); } /**////<summary> /// Description: Check for dormant sockets and close them. ///</summary> ///<param name="eventState">Required parameter for a timer call back /// method.</param> privatevoid CheckSockets(object eventState) ...{ lostTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); try ...{ foreach (StateObject state in connectedSocks) ...{ if (state.workSocket ==null) ...{ // Remove invalid state object Monitor.Enter(connectedSocks); if (connectedSocks.Contains(state)) ...{ connectedSocks.Remove(state); Interlocked.Decrement(ref sockCount); } Monitor.Exit(connectedSocks); } else ...{ if (DateTime.Now.AddTicks(-state.TimeStamp.Ticks).Minute > timeoutMinutes) ...{ RemoveSocket(state); } } } } catch (Exception) ...{ } finally ...{ lostTimer.Change(Server.timerTimeout, Server.timerTimeout); } } /**////<summary> /// Decription: Stop the threads for the port listener. ///</summary> publicvoid Stop() ...{ int lcv; lostTimer.Dispose(); lostTimer =null; for (lcv =0; lcv < numThreads; lcv++) ...{ if (!serverThread[lcv].IsAlive) threadEnd[lcv].Set(); // Set event if thread is already dead } ShuttingDown =true; // Create a connection to the port to unblock the listener thread Socket sock =new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint endPoint =new IPEndPoint(IPAddress.Loopback, this.portNumber); sock.Connect(endPoint); //sock.Close(); sock =null; // Check thread end events and wait for up to 5 seconds. for (lcv =0; lcv < numThreads; lcv++) threadEnd[lcv].WaitOne(5000, false); }
/**////<summary> /// Decription: Open a listener socket and wait for a connection. ///</summary> privatevoid StartListening() ...{ // Establish the local endpoint for the socket. IPEndPoint localEndPoint =new IPEndPoint(IPAddress.Any, this.portNumber); // 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 connections. try ...{ listener.Bind(localEndPoint); listener.Listen(1000); while (!ShuttingDown) ...{ // Set the event to nonsignaled state. allDone.Reset(); // Start an asynchronous socket to listen for connections. listener.BeginAccept(new AsyncCallback(this.AcceptCallback), listener); // Wait until a connection is made before continuing. allDone.WaitOne(); } } catch (Exception e) ...{ threadEnd[0].Set(); } } /**////<summary> /// Decription: Call back method to accept new connections. ///</summary> ///<param name="ar">Status of an asynchronous operation.</param> privatevoid 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 = listener.EndAccept(ar); // Create the state object. StateObject state =new StateObject(); state.workSocket = handler; state.TimeStamp = DateTime.Now; try ...{ Interlocked.Increment(ref sockCount); Monitor.Enter(connectedSocks); connectedSocks.Add(state); Monitor.Exit(connectedSocks); handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.ReadCallback), state); if (sockCount >this.maxSockets) ...{ RemoveSocket(state); //handler.Shutdown(SocketShutdown.Both); //handler.Close(); handler =null; state =null; } } catch (SocketException es) ...{ RemoveSocket(state); } catch (Exception e) ...{ RemoveSocket(state); } }
/**////<summary> /// Decription: Call back method to handle incoming data. ///</summary> ///<param name="ar">Status of an asynchronous operation.</param> protectedvoid ReadCallback(IAsyncResult ar) ...{ String content = String.Empty; // Retrieve the state object and the handler socket // from the async state object. StateObject state = (StateObject) ar.AsyncState; Socket handler = state.workSocket; try ...{ // 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. Monitor.Enter(state); state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); Monitor.Exit(state); // Check for end-of-file tag. // If it is not there, read more data. content = state.sb.ToString(); //if (content.IndexOf("<EOF>") > -1) if ((content.Length >0) && ((content[0] !='<') || ((content[0] =='<') && (content.IndexOf("</message>") >-1)))) ...{ // All the data has been read from the // client. Display it on the console. //Console.WriteLine("Read {0} bytes from socket. Data: {1}", // content.Length, content); // Process the received message state.TimeStamp = DateTime.Now; HandleMessages(state, content); // Echo the data back to the client. // Send(state, content); //state.sb.Length = 0; } else ...{ // Not all data received. Get more. handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.ReadCallback), state); } } else ...{ // Disconnected RemoveSocket(state); } } catch (System.Net.Sockets.SocketException es) ...{ RemoveSocket(state); if (es.ErrorCode !=64) ...{ Console.WriteLine( string.Format("ReadCallback Socket Exception: {0}, {1}.", es.ErrorCode, es.ToString())); } } catch (Exception e) ...{ RemoveSocket(state); if (e.GetType().FullName !="System.ObjectDisposedException") ...{ Console.WriteLine(string.Format("ReadCallback Exception: {0}.", e.ToString())); } } } /**////<summary> /// Decription: Send the given data string to the given socket. ///</summary> ///<param name="sock">Socket to send data to.</param> ///<param name="data">The string containing the data to send.</param> protectedvoid Send(Socket sock, 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. if (byteData.Length >0) sock.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(this.SendCallback), sock); } /**////<summary> /// Author: Asad Aziz /// Decription: Call back method to handle outgoing data. ///</summary> ///<param name="ar">Status of an asynchronous operation.</param> protectedvoid SendCallback(IAsyncResult ar) ...{ // Retrieve the socket from the async state object. Socket handler = (Socket) ar.AsyncState; try ...{ // Complete sending the data to the remote device. int bytesSent = handler.EndSend(ar); } catch (Exception e) ...{ } } /**////<summary> // Description: Find on socket using the identifier as the key. /**////</summary> ///<param name="id">The identifier key associated with a socket.</param> private Socket FindID(string id) ...{ Socket sock =null; Monitor.Enter(connectedHT); if (connectedHT.ContainsKey(id)) sock = (Socket) connectedHT[id]; Monitor.Exit(connectedHT); return sock; } /**////<summary> /// Author: Asad Aziz /// Description: Place the given socket in the connected list. /// /// Notes: /// 1) Get the Member ID from the command /// 2) Add the socket to the connected socket list with its Member ID ///</summary> ///<param name="state">The state object containing the socket info.</param> ///<param name="command">The XML-based message containing the ID that /// needs to be parsed.</param> ///<param name="content">Not used in the base method.</param> virtualprotectedbool StuffList(StateObject state, string command, refstring content) ...{ Socket sock = state.workSocket; string hostID = ReadXML(command, Server.msgConnect, this.connectAttr); if (hostID !=null) ...{ state.id = hostID; if (hostID == Server.webServer) ...{ Console.WriteLine(string.Format("Host control socket connected {0}!", this.title)); returntrue; } // Add to connected list Monitor.Enter(connectedHT); if (connectedHT.ContainsKey(hostID)) ...{ object val = connectedHT[hostID]; connectedHT[hostID] = sock; connectedHT.Add(sock, val); Console.WriteLine(string.Format("Socket found in Hasttable!",this.title)); } else ...{ connectedHT.Add(hostID, sock); connectedHT.Add(sock, hostID); Console.WriteLine(string.Format("Socket not found, adding a new socket to hashtable!",this.title)); } Monitor.Exit(connectedHT); Console.WriteLine(string.Format("Socket was moved to connected {0} list!",this.title)); returntrue; } returnfalse; } /**////<summary> /// Description: Remove the socket contained in the given state object /// from the connected array list and hash table, then close the socket. ///</summary> ///<param name="state">The StateObject containing the specific socket /// to remove from the connected array list and hash table.</param> virtualprotectedvoid RemoveSocket(StateObject state) ...{ Socket sock = state.workSocket; Monitor.Enter(connectedSocks); if (connectedSocks.Contains(state)) ...{ connectedSocks.Remove(state); Interlocked.Decrement(ref sockCount); } Monitor.Exit(connectedSocks); Monitor.Enter(connectedHT); if ((sock !=null) && (connectedHT.ContainsKey(sock))) ...{ object sockTemp = connectedHT[sock]; if (connectedHT.ContainsKey(sockTemp)) ...{ if (connectedHT.ContainsKey(connectedHT[sockTemp])) ...{ connectedHT.Remove(sock); if (sock.Equals(connectedHT[sockTemp])) ...{ connectedHT.Remove(sockTemp); } else ...{ object val, key = sockTemp; while (true) ...{ val = connectedHT[key]; if (sock.Equals(val)) ...{ connectedHT[key] = sockTemp; break; } elseif (connectedHT.ContainsKey(val)) key = val; else// The chain is broken break; } } } else ...{ Console.WriteLine(string.Format("Socket is not in the {0} connected hash table!", this.title)); } } } Monitor.Exit(connectedHT); if (sock !=null) ...{ //if (sock.Connected) // sock.Shutdown(SocketShutdown.Both); sock.Close(); sock =null; state.workSocket =null; state =null; } } }