2016-10-06 49 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); 
} 
相关问题