跟踪WinSock MFC应用程序中的句柄泄漏源

时间:2011-04-14 11:53:32

标签: c++ sockets mfc memory-leaks winsock

我们正在开发一个应用程序,我们使用基于WinSock的sime套接字方法与外部模块进行通信。我们的要求是确保连接始终处于打开状态,因此,每当我们断开连接时,我们会每1分钟连续重试一次。

我们的问题从这里开始。我们观察到,每次重新连接套接字重新连接时,它都会泄漏两个Windows句柄。我们尝试了很多选项,但没有一个可行。哪些句柄可能泄漏,我们怎样才能找出罪魁祸首?

以下是我们现在正在使用的代码:

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;
}

5 个答案:

答案 0 :(得分:3)

尝试Microsoft的免费Process Explorer。它将显示进程的所有打开句柄以及名称(用于文件,互斥锁,事件等句柄)等信息。它还会突出显示新创建和关闭的句柄,因此,如果您单步执行代码循环并刷新显示,则可以看到泄露的确切句柄。

答案 1 :(得分:1)

假设您正确获得了套接字:

m_objLine1.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) 
m_objLine1.m_ClientSocket != INVALID_SOCKET // true

但是,你无法连接,所以

ConnectionResult = connect( *l_ClientSocket, (SOCKADDR*) &clientService,
    sizeof(clientService) ) 
ConnectionResult == SOCKET_ERROR // true

在这种情况下,您应该关闭获取的套接字句柄:

closesocket(m_objLine1.m_ClientSocket);

你有两行,所以我猜你叫这个函数两次,每行一次,所以 这就是两个泄漏手柄的原因。

答案 2 :(得分:0)

我建议您尝试Intel Parallel Inspector以识别内存泄漏及其发生的位置。

如果您想尝试,可以试用下载。

答案 3 :(得分:0)

查找句柄泄漏的一种简单方法是记录所有内容。

每次获得句柄,记录您获得的句柄,以及有关情况的任何其他详细信息。每次释放句柄时,记录您发布的句柄。包括两次实际句柄(只是一些十六进制)。

然后你得到一个看起来像这样的日志(仅举例):

Obtained handle 0xf000 (nLineNo = 5)
Obtained handle 0xb000 (nLineNo = 6)
Obtained handle 0xd0d0 (nLineNo = 7)
Released handle 0xf000
Released handle 0xb000

手动挑选,你可以看到当nLineNo为7时你获得了句柄0xd0d0,它从未被释放。它并不多,但它确实有帮助,如果变得艰难,你甚至可以尝试在每次获取/发布时记录堆栈跟踪。此外,如果始终可靠地生成日志,则可以根据实际值开始输入断点(例如,当句柄为0xd0d0时,在程序中的某个点处中断,这样您就可以看到发生了什么)。

如果它更实用,你可以开始在程序本身内包装你的句柄,例如所有获得的句柄中的std::set,以及有关何时获得它们的任何详细信息,您可以有效地开始攻击您的程序以跟踪它正在执行的操作(然后在修复后撤消所有更改)。

希望有所帮助 - 这是我倾向于至少保留std::set我获得的所有内容的部分原因,因此如果最坏的情况发生在最坏的情况下,您可以在关机时迭代它们并将它们全部释放(并记录下来)大“固定这个!”的消息!)

答案 4 :(得分:0)

尝试在shutdown(SD_BOTH)之后在套接字句柄上添加closesocket();。另外,尝试添加睡眠约100毫秒(仅用于测试),看看它是如何进行的。