使用sockaddr_in的值填充sockaddr_storage结构

时间:2017-02-11 14:22:22

标签: c++ networking struct casting

我有一个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和类似的但是再次没有用。那么我应该如何填充这个结构,以便它保持有效值,同时尊重铸造结构的对齐。

3 个答案:

答案 0 :(得分:4)

您需要先使用适当的地址结构,然后填写:

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成员。

答案 1 :(得分:2)

使用getaddrinfo而不是inet_*系列的地址转换功能几乎总是更好。它会为您完成sockaddr个对象的所有分配和初始化 - 您不必自己弄乱sockaddr_storage。它无缝地处理IPv4和IPv6,它处理&#34; multihomed&#34;具有多个地址的主机,它可以(可选)进行DNS查找。

要使用getaddrinfo,您将完全丢弃您显示的fill函数。 使用 fill的代码可能如下所示:

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中设置标记。)

有关更详细的示例用法,请参阅我链接到的联机帮助页。

答案 2 :(得分:1)

您必须重新解释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)代替您的方法。