C++ 将IPv4/IPv6地址和端口设置为sockaddr_存储结构
我正在将一个IPv4应用程序移植到一个AF独立的代码库(它应该与IPv4和IPv6一起工作)。现在我可以在任何地方使用sockaddr\u存储,但是现在我必须设置(填充)一个sockaddr\u存储。但我不知道正确的方法是什么。以前的代码是:C++ 将IPv4/IPv6地址和端口设置为sockaddr_存储结构,c++,sockets,ip-address,ipv6,C++,Sockets,Ip Address,Ipv6,我正在将一个IPv4应用程序移植到一个AF独立的代码库(它应该与IPv4和IPv6一起工作)。现在我可以在任何地方使用sockaddr\u存储,但是现在我必须设置(填充)一个sockaddr\u存储。但我不知道正确的方法是什么。以前的代码是: // defined in data_socket.h struct sockaddr_in laddr; 现在有一个函数可以设置sin\u addr和sin\u port: void DataSocket::SetLocalAddr(const cha
// defined in data_socket.h
struct sockaddr_in laddr;
现在有一个函数可以设置sin\u addr和sin\u port:
void DataSocket::SetLocalAddr(const char *addr, const int port)
{
this->laddr.sin_port = htons(port);
if(addr != NULL)
this->laddr.sin_addr.s_addr = inet_addr(addr);
else
this->laddr.sin_addr.s_addr = inet_addr("0.0.0.0");
}
void DataSocket::SetLocalAddr(const char *addr, const int port)
{
switch (this->GetAddrFamily(addr)) {
case AF_INET:
(struct sockaddr_in *) this->laddr.sin_port = htons(port);
if(addr != NULL)
inet_pton(AF_INET, addr, (struct sockaddr_in *) this->laddr.sin_addr);
else
inet_pton(AF_INET, "0.0.0.0", (struct sockaddr_in *) this->laddr.sin_addr);
break;
case AF_INET6:
(struct sockaddr_in6 *) this->laddr.sin6_port = htons(port);
if(addr != NULL)
inet_pton(AF_INET6, addr, (struct sockaddr_in6 *) this->laddr.sin6_addr);
else
inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", (struct sockaddr_in6 *) this->laddr.sin6_addr);
break;
default:
return NULL;
}
正如您所看到的,这是旧样式(使用IPv4)
现在我的更改如下。首先,我将中的sockaddr\u更改为sockaddr\u存储
// defined in data_socket.h
struct sockaddr_storage laddr;
然后我更改了上面的代码,以支持IPv4和IPv6:
void DataSocket::SetLocalAddr(const char *addr, const int port)
{
this->laddr.sin_port = htons(port);
if(addr != NULL)
this->laddr.sin_addr.s_addr = inet_addr(addr);
else
this->laddr.sin_addr.s_addr = inet_addr("0.0.0.0");
}
void DataSocket::SetLocalAddr(const char *addr, const int port)
{
switch (this->GetAddrFamily(addr)) {
case AF_INET:
(struct sockaddr_in *) this->laddr.sin_port = htons(port);
if(addr != NULL)
inet_pton(AF_INET, addr, (struct sockaddr_in *) this->laddr.sin_addr);
else
inet_pton(AF_INET, "0.0.0.0", (struct sockaddr_in *) this->laddr.sin_addr);
break;
case AF_INET6:
(struct sockaddr_in6 *) this->laddr.sin6_port = htons(port);
if(addr != NULL)
inet_pton(AF_INET6, addr, (struct sockaddr_in6 *) this->laddr.sin6_addr);
else
inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", (struct sockaddr_in6 *) this->laddr.sin6_addr);
break;
default:
return NULL;
}
}
其中GetAddrFamily()
是:
int DataSocket::GetAddrFamily(const char *addr)
{
struct addrinfo hints, *res;
int status, result;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(addr, 0, &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return false;
}
result = res->ai_family; // This might be AF_INET, AF_INET6,etc..
freeaddrinfo(res); // We're done with res, free it up
return result;
}
看来我的方式是复杂的。这是正确的方法吗?因为我已经将sockaddr_改为sockaddr_存储,实际上我只想让这个问题反过来:
我试图找到最好的解决方案,例如:它说永远不要使用inet\u ntop()
和inet\u pton()
,但是其他一些人(比如Beej的网络教程)说inet\u ntop()
和inet\u pton()
应该用于基于IPv6的应用程序
我的执行方式正确吗?还是应该更改它?如果我理解正确,您将此
转换为第一个成员?不要那样做,说出成员的名字
我还将为这两种情况引入{}
范围和局部变量,使其更易于阅读,例如:
{
struct sockaddr_in * in4 = reinterpret_cast< struct sockaddr_in * >(&this->addr);
in4->laddr.sin_port = htons(port);
... etc
}
{
*in4中的struct sockaddr\u=重新解释cast(&this->addr);
in4->laddr.sin_端口=htons(端口);
等
}
A.因为你使用C++而不是C,所以使用C++风格的演员表。C++中的C样式转换远不明确。p> 我强烈建议让
getaddrinfo
做所有的重担,例如
void DataSocket::SetLocalAddr(const char *addr, const unsigned short int port)
{
struct addrinfo hints, *res;
int status;
char port_buffer[6];
sprintf(port_buffer, "%hu", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* Setting AI_PASSIVE will give you a wildcard address if addr is NULL */
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
if ((status = getaddrinfo(addr, port_buffer, &hints, &res) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return;
}
/* Note, we're taking the first valid address, there may be more than one */
memcpy(&this->laddr, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
}
这就是我喜欢boost::asio::ip::address
的原因之一,它抽象了boost::asio::ip::address\u v4
和boost::asio::ip::address\u v6
。当C获得成员函数时请告诉我。@DeadMG oops对于C
标记表示抱歉,谢谢你的代码。但我对memcpy感到困惑(&this->laddr,res->ai\u addr,res->ai\u addrlen)代码>行。res->ai_addr
是一个sockaddr结构,我猜它会自动转换为sockaddr_in
或sockaddr_in 6
?它是如何存储到this->laddr
(因为this->laddr
是一个sockaddr\u存储
结构)?我想我应该使用port\u缓冲区
而不是getaddrinfo
中的port
,因为getaddrinfo()
排除了常量字符*(又称字符串)?res->ai_addr
指向struct sockaddr
的某个变体,可以是sockaddr_in
或sockaddr_in 6
。没有进行转换,结构只是按原样复制struct sockaddr_storage
的大小和布局允许将其强制转换到任何sockaddr
变体中,所有变体共享一个公共头。re:传递一个字符串作为端口,我不想麻烦,但这是你的代码。(另外,修复了输入错误,getaddrinfo是用来作为缓冲区的)我这样问是因为我将在另一个函数中使用this->laddr
结构。但是我不知道它是基于哪个结构的。我最初将其设置为
sockaddr\u storage。这是否意味着如果我想在其他函数中使用它,我必须现在检查this->laddr.ss_family`(假设它是sockaddr_存储),并将其转换为sockaddr_in`或sockaddr_in 6
?或者我应该检查this->laddr.sa_族
(因为它是一个普通的sockaddr
结构)?@FatihArslan:Checkingladdr.ss_族
和Checking(struct sockaddr)laddr.sa_族
是相同的。在大多数情况下,您只需传递struct sockaddr\u存储
,在需要时将其转换为sockaddr
(或_in/in6变体)。