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

C++ 跟踪WinSock MFC应用程序中的句柄泄漏源,c++,sockets,mfc,memory-leaks,winsock,C++,Sockets,Mfc,Memory Leaks,Winsock,我们正在开发一个应用程序,其中使用基于WinSock的sime套接字方法与外部模块通信。我们的要求是确保连接始终处于打开状态,因此,无论何时断开连接,我们都会每1分钟不断重试一次连接 我们的问题从这里开始。我们观察到,在每次重新连接套接字时,它都会泄漏两个Windows句柄。我们尝试了这么多的选择,但没有一个可行。哪些手柄可能会泄漏,我们如何确定肇事者 以下是我们目前正在使用的代码: bool CSocketClass::ConnectToServer(int nLineNo) { string

我们正在开发一个应用程序,其中使用基于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;
}

我建议您尝试识别内存泄漏及其发生的位置


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

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

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

然后您会得到一个如下所示的日志(仅举个例子):

通过手工查看,您可以看到在Nlinno为7时获得了句柄0xd0d0,但它从未被释放。这并不多,但确实有帮助,如果进展艰难,您甚至可以尝试在每次获取/发布时记录堆栈跟踪。此外,如果日志总是这样可靠地生成,则可以开始根据实际值设置断点(例如,当句柄为0xD0时,在程序中的某个点中断,以便您可以看到它发生了什么)

如果更实用,您可以开始将句柄包装到程序本身中,例如,所有获得的句柄的
std::set
,以及有关获取句柄时间的任何详细信息,并且您可以有效地开始对程序进行黑客攻击,以跟踪它正在执行的操作(修复后撤消所有更改)


希望这能有所帮助-这也是我倾向于至少保留一个
std::set
的原因之一,因此,如果最坏的情况发生,你可以在关机时对其进行迭代并将其全部释放(并记录一个大的“修复此问题!”消息!)

假设你正确获取了套接字:

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);
你有两行,所以我猜你调用这个函数两次,每行一次,所以
这就是为什么两个把手漏了

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

尝试在
closesocket()之后的套接字句柄上添加
关闭(SD\u两者)
另外,尝试添加大约100毫秒的睡眠时间(仅用于测试),看看情况如何。

您的泄漏不在发布的代码中。作为旁注,不要关闭插座(无效的插座)
-这充其量是毫无意义的,最坏的情况是有害的。最初我们只是在插座连接时关闭插座,但即使这样也不能解决我们的目的。。您确定此代码中没有泄漏吗?因为我确实看到(调试时)connect语句之后,我的代码正好泄漏了2个句柄。。为了确保我说的是任务管理器中可以看到的windows句柄泄漏,我不是说内存泄漏。@vrajs5:当你处理完这些套接字后,你不会关闭它们。当你连接时它不是一个漏洞-当你忘记清理时它就变成了一个漏洞。@vrajs5:你是否在调用这个函数之前关闭了旧的插座?@Erik:如果旧的插座断开,我们就调用这些函数。。为了安全起见,我们正在关闭那个旧的套接字,然后调用这个函数。。我知道你想说什么当它没有被清除的时候它就泄漏了。。但是在重新连接之前我们会打电话给closesocket。你是说内存泄漏检测程序吗?因为我说的是句柄泄漏…Intel Parallel Inspector也能够检测泄漏的windows句柄。我用它来查找泄漏的GDI句柄,我想它对套接字句柄也同样有效。我不是手动/直接获取句柄。我只是创建套接字并关闭它。。我还记录了打开和关闭事件(这段代码中没有),但这并不能解决我们的问题。+1 Process Explorer很好,另一个选项是使用sysinternals中的“句柄”工具来检查哪些句柄是打开的
closesocket(m_objLine1.m_ClientSocket);