Tracking down the source of handle leaks in WinSock MFC application
We are developing an application in which we are using a WinSock-based sime socket approach to communicate with an outside module. Our requirement is to make sure that the connection will always be on, so for that reason, we continuously retry to connect every 1 minute whenever we get disconnected.
Our problem starts here. We have observered that on every retry of socket reconnect, it is leaking exactly two Windows handles. We have tried so many options, but none of them are working. Which handles could be leaking, and how could we go about identifying the culprit?
Following is the code that we are using right now:
bool CSocketClass::ConnectToServer(int nLineNo)
{
string strIPAddress;
int nPortNo;
SOCKET* l_ClientSocket;
int ConnectionResult;
//----------------------
// Create a SOCKET for connecting to server
if (nLineNo == 1)
{
m_objLine1.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
strIPAddress = m_objLine1.m_strIPAddress;
nPortNo = m_objLine1.m_nPortNo;
l_ClientSocket = &(m_objLine1.m_ClientSocket);
}
else
{
m_objLine2.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
strIPAddress = m_objLine2.m_strIPAddress;
nPortNo = m_objLine2.m_nPortNo;
l_ClientSocket = &(m_objLine2.m_ClientSocket);
}
if(INVALID_SOCKET == *l_ClientSocket)
{
return false;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( strIPAddress.c_str() );
clientService.sin_port = htons( nPortNo );
//----------------------
// Connect to server.
ConnectionResult = connect( *l_ClientSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) ; if (ConnectionResult == SOCKET_ERROR)
{
if (nLineNo == 1)
{
//ERROR in line1
}
else
{
//ERROR in line2
开发者_开发问答}
return false;
}
else
//In case of successful connection
{
//Other actions
}
return true;
}
Try the free Process Explorer from Microsoft. It will display all the open handles for a process along with information such as name (for file, mutex, event, etc. handles). It will also highlight newly created and closed handles, so if you step through a loop of your code and refresh the display, you can see the exact handles that were leaked.
Let's say you acquired socket correctly:
m_objLine1.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
m_objLine1.m_ClientSocket != INVALID_SOCKET // true
but then, you can't connect, so
ConnectionResult = connect( *l_ClientSocket, (SOCKADDR*) &clientService,
sizeof(clientService) )
ConnectionResult == SOCKET_ERROR // true
in that case, you should close that acquired socket handle:
closesocket(m_objLine1.m_ClientSocket);
You have two lines, so I guess that you call this function twice, once for each line, so that's why two leaked handles.
I would suggest that you try Intel Parallel Inspector in order to identify the memory leaks and where they are occurring.
There is a trial download if you wish to try it.
A simple way to find handle leaks is to log everything.
Every time you obtain a handle, log that you obtained it, as well as any other details about the circumstances. Every time you release a handle, log that you released it. Include both times the actual handle (just some hex).
Then you get a log that looks like this (just for example):
Obtained handle 0xf000 (nLineNo = 5)
Obtained handle 0xb000 (nLineNo = 6)
Obtained handle 0xd0d0 (nLineNo = 7)
Released handle 0xf000
Released handle 0xb000
Picking through this by hand, you can see that you obtained handle 0xd0d0 when nLineNo was 7, and it never got released. It's not much but it does help, and if the going gets tough, you can even try logging stack traces at each obtain/release. Also, if the log is always reliably produced like that, you can start putting in breakpoints based on the actual values (e.g. break at a point in the program when the handle is 0xd0d0, so you can see what's happening to it).
If it's more practical, you can start wrapping your handles inside the program itself, e.g. a std::set
of all obtained handles, along with any details about when they were obtained, and you can effectively start hacking your program to keep track of what it's doing (then undo all your changes once you fixed it).
Hope that helps - it's part of the reason I tend to at least keep a std::set
of everything I obtain, so if worst comes to worst you can iterate over them on shutdown and release them all (and log a big "FIX THIS!" message!)
Try adding a shutdown(SD_BOTH)
on the socket handles after the closesocket();
Also, try adding a Sleep for about 100ms (only for a test) and see how it goes.
精彩评论