我正在编写一些套接字代码并基于某些使用IPv4或IPv6的参数。为此,我有一个这样的代码:
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_in6
重新解释为sockaddr
?
答案 0 :(得分:2)
如果代码size
的第一个版本是sizeof(addr6)
(正如您在评论中所述),则代码的第一个版本使用memcpy
复制{{1} }数据字节。
代码的第二个版本使用常规sizeof(struct sockaddr_in6)
赋值来仅复制struct sockaddr
个字节。
sizeof(struct sockaddr)
小于sizeof(struct sockaddr)
,这使得这两个代码示例不同。
请注意,在第一个版本中,sizeof(struct sockaddr_in6)
中的收件人对象属于memcpy
类型,即它小于复制的字节数。发生内存溢出,这会破坏存储在相邻内存位置的其他一些数据。代码“只能偶然”起作用。即如果这个位“有效”,那么另一段代码(依赖于现在被破坏的数据的代码)可能会失败。
答案 1 :(得分:1)
sockaddr
不足以保存sockaddr_in6
的数据。第一个代码示例“工作”只是因为源地址数据被完整复制,并且您将完整地址传递给bind()
,但您在复制期间也已经破坏了堆栈内存。第二个代码示例不起作用,因为它在分配期间截断了地址数据,但它不再破坏堆栈内存。
这两个代码示例都不能正常用于IPv6,但两者都可以“正常”用于IPv4,因为sockaddr
足够大以容纳来自sockaddr_in
的数据,因此不会发生垃圾或截断。< / p>
为了确保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);
}