c++ 使用sockaddr_in的值填充sockaddr_storage结构

xv8emn3q  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(371)

我有一个sockaddr_storage对象,我需要用用户提供的值填充它。请注意,用户可以提供AF_INETAF_INET6作为填充结构体的域。

void fill(sockaddr_storage &addrStruct, int domain,
      const char addr[], const int port)
{
    std::memset(&addrStruct, 0, sizeof(addrStruct));

    switch(domain) {

        case AF_INET: addrStruct.sin_family = AF_INET;
                      addrStruct.sin_port= htons(port);

                      inet_pton(AF_INET, addr, addrStruct.sin_addr);

        case AF_INET6: ....
        ....
        ....
        default: ....
    }
}

字符串
很确定这不起作用,因为addrStruct的类型是struct sockaddr_storage,而这些成员存在于struct sockaddr_in中。我也试过static_cast<sockaddr_in>(addrStruct).sin_port和类似的,但同样不起作用。那么我应该如何填充这个结构体,使它在保持有效值的同时,尊重转换结构体的对齐。

wecizke3

wecizke31#

您需要首先使用适当的地址结构,并填写它:

struct sockaddr_in sin;

sin.sin_family = AF_INET;
sin.sin_port= htons(port);

字符串
然后,完成后,将memcpy()放入sockaddr_storage

memcpy(reinterpret_cast<char *>(&addrStruct),
       reinterpret_cast<char *>(&sin), sizeof(sin));


请注意,您仍然应该像以前一样,预先将整个sockaddr_storage缓冲区清零。
另一种方法是定义所有地址结构的union,包括sockaddr_storage,然后初始化相应的union成员。

wz1wpwve

wz1wpwve2#

使用getaddrinfo几乎总是比使用inet_*系列地址转换函数更好。它会为您完成sockaddr对象的所有分配和初始化您不必亲自处理sockaddr_storage。它无缝地处理IPv4和IPv6,它处理具有多个地址的“多宿主”主机,并且它可以(可选地)进行DNS查找。
要使用getaddrinfo,您完全放弃了所展示的fill函数。usesfill的代码可能看起来像这样:

struct sockaddr_storage ss;
fill(&ss, AF_INET, "10.0.0.21", 25);
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
     perror("socket");
     return -1;
}
if (connect(sock, (struct sockaddr*)&ss, sizeof(struct sockaddr_in)) {
     perror("10.0.0.21:25");
     return -1;
}
/* use connected sock here */

字符串
你把它换成

struct addrinfo hints;
struct addrinfo *rp, *result;
memset(hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;

int err = getaddrinfo("10.0.0.21", "25", &hints, &result);
if (err) {
    if (err == EAI_SYSTEM) {
        fprintf(stderr, "10.0.0.21:25: %s\n", strerror(errno));
    } else {
        fprintf(stderr, "10.0.0.21:25: %s\n", gai_strerror(err));
    }
    return 1;
}

int sock;
for (rp = result; rp; rp = rp->ai_next) {
    sock = socket(rp->ai_family, rp->ai_socktype,
                  rp->ai_protocol);
    if (sock == -1)
        continue;
    if (connect(sock, rp->ai_addr, rp->ai_addrlen))
        break;
    close(sock);
}
if (rp == 0) {
    perror("10.0.0.21:25");
    return -1;
}
freeaddrinfo(result);
/* use sock here */


这看起来更复杂,但请记住,它完全取代了您不知道如何编写的fill函数,并且还记得它将无缝地为您处理DNS。(如果您有具体的理由只想处理数字地址,您可以在hints中为此设置标志。
更详细的示例用法请参阅我链接的手册页。

uoifb46i

uoifb46i3#

你必须重新解释addrStructsockaddr_in的转换。在你的例子中,代码如下:

case AF_INET: 
    {
        struct sockaddr_in * tmp =
            reinterpret_cast<struct sockaddr_in *> (&addrStruct);
        tmp->sin_family = AF_INET;
        tmp->sin_port = htons(port);
        inet_pton(AF_INET, addr, tmp->sin_addr);
    }
    break;

字符串
但是我建议你使用getaddrinfo(3)来代替你的方法。

相关问题