C++ 将IPv4/IPv6地址和端口设置为sockaddr_存储结构

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

我正在将一个IPv4应用程序移植到一个AF独立的代码库(它应该与IPv4和IPv6一起工作)。现在我可以在任何地方使用sockaddr\u存储,但是现在我必须设置(填充)一个sockaddr\u存储。但我不知道正确的方法是什么。以前的代码是:

// defined in data_socket.h
struct sockaddr_in laddr;
现在有一个函数可以设置sin\u addrsin\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;
然后我更改了上面的代码,以支持IPv4IPv6

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:Checking
laddr.ss_族
和Checking
(struct sockaddr)laddr.sa_族
是相同的。在大多数情况下,您只需传递
struct sockaddr\u存储
,在需要时将其转换为
sockaddr
(或_in/in6变体)。