Asynchronous server socket using C#

来源:互联网 发布:网络剧最好的我们 编辑:程序博客网 时间:2024/05/22 16:58
/// <summary>
    
/// Author: Asad Aziz
    
/// Description: State object for reading client data asynchronously.
    
/// </summary>


    
public class StateObject
    
{
        
public bool connected = false;    // ID received flag
        public Socket workSocket = null;    // Client socket.
        public Socket partnerSocket = null;    // Partner socket.
        public const int BufferSize = 1024;    // Size of receive buffer.
        public byte[] buffer = new byte[BufferSize];// Receive buffer.
        public StringBuilder sb = new StringBuilder();//Received data String.
        public string id = String.Empty;    // Host or conversation ID
        public DateTime TimeStamp;
    }


    
///<summary>
    
/// Author: Asad Aziz
    
/// Description: Server is the class to control sockets  
    
///</summary>

    public class Server
    
{
        
protected int portNumber;
        
protected int maxSockets;
        
protected int sockCount = 0;
        
private int convID = 0;
        
private Timer lostTimer;
        
private const int numThreads = 1;
        
private const int timerTimeout = 300000;
        
private const int timeoutMinutes = 3;
        
private bool ShuttingDown = false;
        
protected string 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>


        
public void 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>

        private void 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>

        public void 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(
5000false);
        }


 

 

/// <summary>
        
/// Decription: Open a listener socket and wait for a connection.
        
/// </summary>


        
private void 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>

        private 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 
= 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>

        protected void 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>

        protected void 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>

        protected void 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>




        
virtual protected bool StuffList(StateObject state, string command,
            
ref string 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));
                    
return true;
                }


                
// 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));
                
return true;
            }

            
return false;
        }


        
/// <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>

        virtual protected void 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;
                                }

                                
else if (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;
        
            }

        }

    }
原创粉丝点击