MILIND_JOG
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
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;
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.hashTableUnit s[unitId];
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.hashTableUnit
ASKER
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.
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.
ASKER
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.Coun t;
SocketAsyncEventArgs[] array4Purge = new SocketAsyncEventArgs[listc ount4];
Program.listOfSockets.Copy To(array4P urge);
foreach (SocketAsyncEventArgs socket4 in array4Purge)
{
theUserTokenHere = (DataHoldingUserToken)sock et4.UserTo ken;
//unitInKey = theUserTokenHere.unitID;
double timeSincePrevData = calcTimeInterval(theUserTo kenHere);
if (timeSincePrevData > 0.8)
{
socket4.SocketError = SocketError.NotConnected;
socket4.AcceptSocket.Shutd own(Socket Shutdown.B oth);
Program.listOfSockets.Remo ve(socket4 );
if (TRInitModule.hashTableUni ts.Contain sValue(soc ket4))
{
lock (TRInitModule.hashTableUni ts.SyncRoo t)
{
TRInitModule.hashTableUnit s.Remove(s ocket4);
}
}
}
}
Thanks !!
Milind
int listcount4 = Program.listOfSockets.Coun
SocketAsyncEventArgs[] array4Purge = new SocketAsyncEventArgs[listc
Program.listOfSockets.Copy
foreach (SocketAsyncEventArgs socket4 in array4Purge)
{
theUserTokenHere = (DataHoldingUserToken)sock
//unitInKey = theUserTokenHere.unitID;
double timeSincePrevData = calcTimeInterval(theUserTo
if (timeSincePrevData > 0.8)
{
socket4.SocketError = SocketError.NotConnected;
socket4.AcceptSocket.Shutd
Program.listOfSockets.Remo
if (TRInitModule.hashTableUni
{
lock (TRInitModule.hashTableUni
{
TRInitModule.hashTableUnit
}
}
}
}
Thanks !!
Milind
ASKER
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.