2016-10-06 51 views
-2

我正在寫一些套接字代碼,並基於我使用IPv4或IPv6的一些參數。爲此,我有這樣的代碼:爲什麼在memcpy工作時reinterpret_cast失敗?

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

那麼它bindCannot 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

我想你誤解了。我刪除了大部分初始化代碼。代碼工作正常(套接字綁定和正常工作)與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

「作品」意味着你寫過的東西是......進一步堆疊?所以你重寫了先前分配的堆棧中的東西?在一個足夠糟糕的情況下,它會寫在你的返回碼地址,對不對? – BenPen

+0

此外,你也受到破壞。 – BenPen

1

sockaddr不足以容納來自的數據。第一個代碼示例「有效」僅僅是因爲源地址數據正在被完全複製,並且您正在將完整地址傳遞到bind(),但在複製過程中也會丟棄堆棧內存。第二個代碼示例不起作用,因爲它在分配過程中截斷了地址數據,但它不再破壞堆棧內存。

這兩個代碼示例都不能正常工作於IPv6,但兩者都能「正常」工作,因爲sockaddr足夠大以容納來自sockaddr_in的數據,因此不會發生垃圾或截斷事件。

爲了確保final_addr是大到足以容納從任sockaddr_in或數據,它需要被聲明爲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); 
} 
相關問題