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

MILIND_JOGAsked:
Who is Participating?
 
ambienceCommented:
For one, this defeats the purpose of a dictionary

                foreach (DictionaryEntry entry in TRInitModule.hashTableUnits)
                {
                    Int64 currentUnitID = Convert.ToInt64(entry.Key);
                    if (currentUnitID == unitID)

Open in new window


This is how it should be

if (TRInitModule.hashTableUnits.ContainsKey(unitID))

Also, "AcceptSocket.Shutdown(SocketShutdown.Both);" will try to flush buffers and most likely wont do anything if the socket has  been idle for some time. You should call Socket.Close() because that would free the resources associated with the socket. In fact, you may choose not to call Shutdown() if a client will only establish one connection at a time and is found reconnecting.
0
 
MILIND_JOGAuthor Commented:
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.
0
 
ambienceCommented:
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];
0
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

 
MILIND_JOGAuthor Commented:
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?
0
 
ambienceCommented:
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.
0
 
MILIND_JOGAuthor Commented:
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
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.