2016-10-06 7 views
-2

Я пишу код сокета и основываясь на некоторых параметрах Я использую либо IPv4, либо IPv6. Для этого у меня есть код:Почему reinterpret_cast не работает, пока работает memcpy?

struct sockaddr final_addr; 
... 
struct sockaddr_in6 addr6; 
... 
memcpy(&final_addr, &addr6, size); 
... 
bind(fd, &final_addr, size); 

Это прекрасно работает. Однако, если я (что моя первоначальная идея)

struct sockaddr final_addr; 
... 
struct sockaddr_in6 addr6; 
... 
final_addr = *reinterpret_cast<struct sockaddr*>(&addr6); 
... 
bind(fd, &final_addr, size); 

, то он не на bind с Cannot assign requested address ошибки.

Обратите внимание, что этот неправильный код работает нормально, если я переключаюсь на IPv4 sockaddr_in.

Что здесь происходит? Почему я не могу просто переосмыслить как sockaddr?

+0

'sockaddr' является непрозрачным указатель, который либо поддерживает в' sockaddr_in' (IPv4) или 'sockaddr_in6' (IPv6). Помимо поля 'sin_family' эти структуры совершенно разные. Ни 'reinterpret_cast', ни' memcpy() 'не будут работать правильно, чтобы конвертировать из одного в другой. –

+0

У sockaddr_in6 есть ключевое слово const с определением? – BenPen

+1

Я думаю, вы неправильно поняли. Ive удалил большую часть кода инициализации. Код работает нормально (сокеты привязаны и работают правильно) с memcpy. Однако он терпит неудачу с reinterpret_cast. – freakish

ответ

2

Если в первой версии кода size является sizeof(addr6) (как указано в комментариях), то первый вариант кода использует memcpy скопировать sizeof(struct sockaddr_in6) байт данных.

Вторая версия кода использует регулярное назначение struct sockaddr для копирования только sizeof(struct sockaddr) байтов.

sizeof(struct sockaddr) меньше, чем sizeof(struct sockaddr_in6), что делает эти два образца кода разными.

Обратите внимание, что в первой версии объект-получатель в этом memcpy имеет тип struct sockaddr, то есть он меньше, чем количество копируемых байтов. Происходит переполнение памяти, которое захватывает некоторые другие данные, хранящиеся в соседних ячейках памяти. Код «работает» только случайно. То есть если этот бит «работает», тогда какой-то другой фрагмент кода (тот, который опирается на теперь сбитые данные), скорее всего, потерпит неудачу.

+0

«works» означает, что вы написали материал, который был ... далее в стеке? так что вы перезаписывали материал в стеке, выделенный ранее? В достаточно плохом случае он будет писать по всему обратному коду, верно? – BenPen

+0

Также вы также подвергаетесь скрежетанию. – BenPen

1

sockaddr не достаточно большой, чтобы хранить данные от . Первый пример кода «работает» только потому, что данные адреса источника полностью копируются, и вы передаете полный адрес bind(), но вы также уничтожили стек памяти во время копирования. Второй пример кода не работает, поскольку он усекает данные адреса во время назначения, но он больше не уничтожает стек.

Ни один из примеров кода не будет работать корректно для IPv6, но оба будут работать «OK» для IPv4, потому что sockaddr достаточно велик, чтобы хранить данные с sockaddr_in, поэтому не происходит ни перетаскивания, ни усечения.

Для обеспечения final_addr достаточно большой, чтобы держать данные либо sockaddr_in или , он должен быть объявлен как sockaddr_storage вместо, который гарантированно будет достаточно большим, чтобы вместить данные любого типаsockaddr_... STRUCT:

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

 Смежные вопросы

  • Нет связанных вопросов^_^