sockaddr
недостаточно велик для хранения данных из sockaddr_in6
. Первый пример кода «работает» только потому, что данные исходного адреса копируются полностью, и вы передаете полный адрес в bind()
, но при копировании вы также испортили память стека. Второй пример кода не работает, потому что он усекает адресные данные во время назначения, но больше не уничтожает память стека.
Ни один из примеров кода не будет работать правильно для IPv6, но оба будут "работать" нормально для IPv4, потому что sockaddr
достаточно велик для хранения данных из sockaddr_in
, поэтому не происходит уничтожения или усечения.
Чтобы гарантировать, что final_addr
достаточно большой для хранения данных из sockaddr_in
или sockaddr_in6
, вместо этого его необходимо объявить как sockaddr_storage
, который гарантированно будет достаточно большим для хранения данных из любого типа структуры sockaddr_...
:
struct sockaddr_storage final_addr;
int size;
if (use IPv6)
{
struct sockaddr_in6 addr6;
// populate addr6 as needed...
memcpy(&final_addr, &addr6, sizeof(addr6));
or
*reinterpret_cast<struct sockaddr_in6*>(&final_addr) = addr6;
size = sizeof(addr6);
}
else
{
struct sockaddr_in addr4;
// populate addr4 as needed...
memcpy(&final_addr, &addr4, sizeof(addr4));
or
*reinterpret_cast<struct sockaddr_in*>(&final_addr) = addr4;
size = sizeof(addr4);
}
bind(fd, reinterpret_cast<struct sockaddr*>(&final_addr), size);
Лучшим вариантом было бы использовать getaddrinfo()
или эквивалент для создания подходящего блока памяти sockaddr_...
для вас:
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC;
struct addrinfo *addr = NULL;
if (getaddrinfo("ip address here", "port here", &hints, &addr) == 0)
{
bind(fd, addr->ai_addr, addr->ai_addrlen);
freeaddrinfo(addr);
}
В качестве альтернативы:
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = ...; // SOCK_STREAM, SOCK_DGRAM, etc...
hints.ai_protocol = ...; // IPPROTO_TCP, IPPROTO_UDP, etc...
struct addrinfo *addrs = NULL;
if (getaddrinfo(NULL, "port here", &hints, &addrs) == 0)
{
for (struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
{
int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (fd != -1)
{
bind(fd, addr->ai_addr, addr->ai_addrlen);
// save fd somewhere for later use
...
}
}
freeaddrinfo(addrs);
}
person
Remy Lebeau
schedule
06.10.2016
sockaddr
— это непрозрачный указатель, который поддерживает либоsockaddr_in
(IPv4), либоsockaddr_in6
(IPv6). Помимо поляsin_family
эти структуры совершенно разные. Ниreinterpret_cast
, ниmemcpy()
не будут работать правильно для преобразования одного в другое. - person πάντα ῥεῖ   schedule 06.10.2016sockaddr_in6*
кsockaddr*
дляbind
(например, stackoverflow.com/questions/13504934/ ) Как бы вы назвалиbind
иначе? Или это кастинг через()
делает что-то другое? - person freakish   schedule 06.10.2016size
в первой версии? - person AnT   schedule 06.10.2016sockaddr
просто идентиченsockkaddr_in
: pubs.opengroup.org/onlinepubs /009696699/базефс/систем/ - person πάντα ῥεῖ   schedule 06.10.2016sizeof(addr6)
. Я дважды проверил размеры, они правильные. В противном случае я бы не ожидал, что это сработает в любом случае. - person freakish   schedule 06.10.2016sizeof(struct sockaddr_in6)
байт, а вторая копируетsizeof(struct sockaddr)
байта. - person AnT   schedule 06.10.2016memcpy
. - person freakish   schedule 06.10.2016