Сбой кода Winsock в freeaddrinfo

#c #sockets #networking

#c #сокеты #сеть

Вопрос:

У меня возникли проблемы с некоторым сетевым кодом, который использует freeaddrinfo (…) с Visual Studio 2012 x64. Код выглядит примерно так:

 struct addrinfo hints = {0};
hints.ai_family = AF_INET;      // only use IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags =  AI_ADDRCONFIG;

struct addrinfo *res = nullptr;
int status = getaddrinfo(_rIP.c_str(), std::to_string(_port).c_str(), amp;hints, amp;res);
if (status == 0)
{
    addDestAddress(res->ai_addr);
}

freeaddrinfo(res);
  

getaddrinfo / freeaddrinfo используется в нескольких местах по всему коду, и я получаю постоянные
сбои в режиме выпуска. Код для addDestAddress выглядит следующим образом:

 void addDestAddress(const sockaddr *_pAddr)
{
    m_vecDestAddrs.push_back(*(sockaddr_storage*)_pAddr);
}
  

Правильно ли это использование freeaddrinfo? Если я удалю вызовы freeaddrinfo, все работает отлично, за исключением возможных утечек памяти.

РЕДАКТИРОВАТЬ Этот код теперь работает отлично. Отредактировано в соответствии с ответом Реми Лебо.

 struct addrinfo hints = {0};
hints.ai_family = AF_INET;  // only use IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags =  AI_ADDRCONFIG;

struct addrinfo *res = nullptr;
int status = getaddrinfo(_rIP.c_str(), std::to_string(_port).c_str(), amp;hints, amp;res);
if (status == 0)
{
    if (res->ai_family == AF_INET)
    {
        addDestAddress((sockaddr_in*)res->ai_addr);
    }

    freeaddrinfo(res);
}
  

Тогда код для addDestAddress выглядит следующим образом:

 void addDestAddress(const sockaddr_in *_pAddr)
{
    sockaddr_storage ssaddr = {0};
    memcpy(amp;ssaddr, _pAddr, sizeof(sockaddr_in));
    m_vecDestAddrs.push_back(ssaddr);
}
  

Это используется только для работы с IPv4 UDP, и поддержка IPv6 не требуется.

Комментарии:

1. Ваш код очень расплывчатый, возможно, вам следует попытаться сконденсировать ваш реальный код в SSCCE . Возможно, вы неправильно сопоставили getaddrinfo freeaddrinfo вызовы and или проигнорировали код состояния. Реальная проблема не видна из вашего фрагмента кода, но она возникнет, когда вы попытаетесь свести свой фактический код к нескольким строкам.

Ответ №1:

Из документации, с моим акцентом:

Память, выделенная при успешном вызове этой функции, должна быть освобождена с последующим вызовом freeaddrinfo.

Вы не прислушиваетесь к этой инструкции. Вы вызываете freeaddrinfo безоговорочно. Вы должны вызывать только freeaddrinfo тогда, когда вызов выполнен getaddrinfo успешно.

Комментарии:

1. Спасибо за информацию, но, к сожалению, в моем случае это не помогло.

2. Это все, что очевидно из предоставленного вами кода. Пока вы не предоставите SSCCE, не ожидайте большего, чем то, что я написал.

3. Я не смог воспроизвести сбой вне приложения. Когда я врываюсь в код после сбоя, он всегда заканчивается вызовами freeaddrinfo, и приложение работает нормально без вызовов. Я продолжу тестирование и опубликую SSCCE, как только он у меня появится.

4. Какие getaddrinfo() реализации на самом деле выполняют грязное дело записи мусора в выходной указатель, когда не возвращается успех? glibc не: github.com/bminor/glibc/blob/glibc-2.34/sysdeps/posix /…

Ответ №2:

Вы допускаете две ошибки:

  1. вы вызываете freeaddrinfo() , даже если getaddrinfo() происходит сбой. Не делайте этого.

  2. вы предполагаете, что res->ai_addr это a sockaddr_storage* , но это не так. Это a sockaddr_in* вместо, потому hints.ai_family что is AF_INET (если бы вы использовали AF_INET6 , это было бы sockaddr_in6 вместо, и AF_UNSPEC могло быть любым из них). sockaddr_in меньше, чем sockaddr_storage , поэтому вы неправильно обращаетесь к памяти при копировании ai_addr данных в свой вектор.

Попробуйте это вместо:

 struct addrinfo hints = {0};
hints.ai_family = AF_INET;      // only use IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags =  AI_ADDRCONFIG;

struct addrinfo *res = nullptr;
int status = getaddrinfo(_rIP.c_str(), std::to_string(_port).c_str(), amp;hints, amp;res);
if (status == 0)
{
    addDestAddress((sockaddr_in*) res->ai_addr);
    freeaddrinfo(res);
}

void addDestAddress(const sockaddr_in *_pAddr)
{
    sockaddr_storage ssaddr = {0};
    memcpy(amp;ssaddr, _pAddr, sizeof(sockaddr_in));
    m_vecDestAddrs.push_back(ssaddr);
}
  

Или это:

 struct addrinfo hints = {0};
hints.ai_family = AF_INET;      // only use IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags =  AI_ADDRCONFIG;

struct addrinfo *res = nullptr;
int status = getaddrinfo(_rIP.c_str(), std::to_string(_port).c_str(), amp;hints, amp;res);
if (status == 0)
{
    addDestAddress(res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);
}

void addDestAddress(const sockaddr *_pAddr, int addrlen)
{
    switch (addrlen)
    {
        case sizeof(sockaddr_in):
        case sizeof(sockaddr_in6):
        {
            sockaddr_storage ssaddr = {0};
            memcpy(amp;ssaddr, _pAddr, addrlen);
            m_vecDestAddrs.push_back(ssaddr);
            break;
        }
    }
}
  

Комментарии:

1. Спасибо, я обновил свой код, чтобы использовать правильные размеры структуры адресов, и теперь все работает отлично