Link to home
Start Free TrialLog in
Avatar of MILIND_JOG
MILIND_JOGFlag for India

asked on

Closing a TCP Asynch socket C#

I have a TCP socket application based on examples of asynch socket programming available on Microsoft site and an example on Code Project.  
My client is a GPRS based device which does not generally do a clean close on the TCP connection.   I need to remove these idle connections on my TCP server.  Am maintaining a hashtable in the server that keeps a record of the unique unitID sent by the client and the associated socket.  When the client reconnects -I need to course through the hashtable and close the socket related with the previous client ID and remove the entry in the hashtable and add this new entry with the current socket.  
My question is how to cleanly and surely remove a socket and how to manage the hashtable while doing so

My code is as follows

private bool removePrevSocketIfPresent(Hashtable hashTableUnits, long unitID)
        {
            bool allWEll = false;
            SocketAsyncEventArgs tempSocketToremove;
            DataHoldingUserToken tempTokenToRemove;
           //locking the hashtable for the whole operation
            lock (TRInitModule.hashTableUnits.SyncRoot)
            {
                bool markForRemoval = false;
                foreach (DictionaryEntry entry in TRInitModule.hashTableUnits)
                {
                    Int64 currentUnitID = Convert.ToInt64(entry.Key);
                    if (currentUnitID == unitID)
                    {
                        try
                        {
                            tempSocketToremove = (SocketAsyncEventArgs)(entry.Value);
                            tempTokenToRemove = (DataHoldingUserToken)tempSocketToremove.UserToken;
                            tempSocketToremove.SocketError = SocketError.OperationAborted;
                            tempSocketToremove.AcceptSocket.Shutdown(SocketShutdown.Both);

                            //is the above line enough or do I need to do the below lines too?
                           // tempSocketToremove.AcceptSocket.Close();
                           // tempTokenToRemove.Reset();
                            //if (tempTokenToRemove.theDataHolder.dataMessageReceived != null)
                            //{
                            //    tempTokenToRemove.CreateNewDataHolder();
                            //}
                         markForRemoval = true;
                            
                                Console.WriteLine();
                                Console.WriteLine("One socket removed, count is = " + TRInitModule.hashTableUnits.Keys.Count);
                            allWEll = true;
                        }
                        catch (Exception eRT)
                        {
                            allWEll = false;
                            Console.WriteLine("ERROR IN  REMOVE ITEM  " + eRT.Message);
                        }

                    }
                    else
                    {
                        markForRemoval = false;
                    }
                }
                if (markForRemoval)
                {
                    TRInitModule.hashTableUnits.Remove(unitID);
                }
            }
            return allWEll;
        }

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of ambience
ambience
Flag of Pakistan image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of MILIND_JOG

ASKER

Please explain why Socket.Shutdown() is not needed.  

Also, once I know that the hashtable contains the unitID - I need to first close the socket with that key - so how do I get to that socket without running a foreach loop.

Thanks for your help though.
Shutdown() is needed to make sure any pending data is received and sent, but since your device is "re-connecting" it wouldnt work since its establishing a new connection. You call shutdown in following scenario for example

socket.send(.......)
socket.shutdown()  -> make sure previous send is completed
socket.close()

-

This is how you should get the socket for dictionary.

var socketToRemove = TRInitModule.hashTableUnits[unitId];
I also need to check for idle sockets ( more than 5 minutes) and remove them.  Which is the best property to use to check for idle sockets?
Well, a simple way is to keep a timestamp with the device specific data, like say

DateTime mLastActivityTime;

Update this every time you receive anything from the device.

You can then have a separate thread run at regular intervals to run over all connected devices and close those been idle for over 5 minutes.
Wow that means I am on the right path. I have done just that.  Please tell me if the closing is alright is code below. I have  a list of all sockets plus a hashtable with unitID as key and the current live socket as I need to be able to send specific commands to the unit from time to time.  

 int listcount4 = Program.listOfSockets.Count;
                SocketAsyncEventArgs[] array4Purge = new SocketAsyncEventArgs[listcount4];
                Program.listOfSockets.CopyTo(array4Purge);

                foreach (SocketAsyncEventArgs socket4 in array4Purge)
                {
                    theUserTokenHere = (DataHoldingUserToken)socket4.UserToken;
                    //unitInKey = theUserTokenHere.unitID;
                    double timeSincePrevData = calcTimeInterval(theUserTokenHere);
                    if (timeSincePrevData > 0.8)
                    {
                        socket4.SocketError = SocketError.NotConnected;
                        socket4.AcceptSocket.Shutdown(SocketShutdown.Both);
                        Program.listOfSockets.Remove(socket4);
                        if (TRInitModule.hashTableUnits.ContainsValue(socket4))
                        {
                            lock (TRInitModule.hashTableUnits.SyncRoot)
                            {
                                TRInitModule.hashTableUnits.Remove(socket4);
                            }
                        }
                    }
                }

Thanks !!
Milind