Отслеживание источника утечек в приложении WinSock MFC - PullRequest
4 голосов
/ 14 апреля 2011

Мы разрабатываем приложение, в котором мы используем основанный на 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 ]

3 голосов
/ 04 мая 2011

Попробуйте бесплатный Process Explorer от Microsoft.Он отобразит все открытые дескрипторы процесса вместе с информацией, такой как имя (для файловых, мьютексных, событийных и т. Д. Дескрипторов).Также будут выделены вновь созданные и закрытые дескрипторы, поэтому, если вы выполните цикл вашего кода и обновите отображение, вы сможете увидеть точные дескрипторы, которые были пропущены.

1 голос
/ 02 мая 2011

Допустим, вы правильно получили сокет:

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

У вас есть две строки, поэтому я полагаю, что вы вызываете эту функцию дважды, один раз для каждой строки, поэтому две ручки с утечкой.

0 голосов
/ 05 мая 2011

Попробуйте добавить shutdown(SD_BOTH) на дескрипторы сокетов после closesocket(); Кроме того, попробуйте добавить режим сна примерно на 100 мс (только для теста) и посмотрите, как он работает.

0 голосов
/ 30 апреля 2011

Простой способ найти утечки ручек состоит в том, чтобы регистрировать все.

Каждый раз, когда вы получаете ручку, регистрируйте ее, а также любые другие подробности об обстоятельствах. Каждый раз, когда вы отпускаете ручку, регистрируйте, что вы выпустили ее. Включите оба раза фактический дескриптор (только некоторые шестнадцатеричные).

Тогда вы получите журнал, который выглядит так (только для примера):

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

Выбрав это вручную, вы можете видеть, что вы получили дескриптор 0xd0d0, когда nLineNo было 7, и он никогда не был выпущен. Это не так много, но это помогает, и если дела идут плохо, вы можете даже попробовать записывать трассировки стека при каждом получении / выпуске. Кроме того, если журнал всегда надежно создается таким образом, вы можете начать вводить точки останова на основе фактических значений (например, разрыв в точке в программе, когда дескриптор равен 0xd0d0, чтобы вы могли видеть, что с ним происходит).

Если это более практично, вы можете начать оборачивать свои дескрипторы в самой программе, например std::set всех полученных дескрипторов, а также любые подробности о том, когда они были получены, и вы можете эффективно приступить к взлому вашей программы, чтобы отследить, что она делает (затем отменить все ваши изменения, как только вы ее исправили).

Надеюсь, что это помогает - это одна из причин, по которой я стремлюсь, по крайней мере, сохранить std::set всего, что получаю, поэтому, если худший из них окажется худшим, вы можете перебрать их при завершении работы и отпустить их все (и записать большой файл) ИСПРАВИТЬ ЭТО! "Сообщение!)

0 голосов
/ 30 апреля 2011

Я бы посоветовал вам попробовать Intel Parallel Inspector , чтобы определить утечки памяти и определить, где они происходят.

Существует пробная загрузка, если вы хотите попробовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...