#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:
Вы допускаете две ошибки:
-
вы вызываете
freeaddrinfo()
, даже еслиgetaddrinfo()
происходит сбой. Не делайте этого. -
вы предполагаете, что
res->ai_addr
это asockaddr_storage*
, но это не так. Это asockaddr_in*
вместо, потомуhints.ai_family
что isAF_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. Спасибо, я обновил свой код, чтобы использовать правильные размеры структуры адресов, и теперь все работает отлично