【问题标题】:How to create sockaddr from sockaddr_in / sockaddr_in6 structure如何从 sockaddr_in / sockaddr_in6 结构创建 sockaddr
【发布时间】:2019-09-04 08:39:53
【问题描述】:

下面是 IPv6 和 IPv4 客户端代码示例代码的摘录:

IPv6

int s;
struct sockaddr_in6 addr;

s = socket(AF_INET6, SOCK_STREAM, 0);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(5000);
inet_pton(AF_INET6, "::1", &addr.sin6_addr);
connect(s, (struct sockaddr *)&addr, sizeof(addr));

IPv4

int s;
struct sockaddr_in addr;

s = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
servaddr.sin_addr.s_addr = INADDR_ANY; 
connect(s, (struct sockaddr *)&addr, sizeof(addr));

我的目标是为 IPv6 和 IPv4 地址编写用户定义的连接 API。由于它们都将结构转换为 struct sockaddr 结构 - 我们如何以编程方式将 sockaddr_insockaddr_in6 转换为 sockaddr结构(填充其成员变量),然后用 sockaddr sturcure 调用 connect?

【问题讨论】:

  • 我怀疑,memcpy(&addr, &addr_in, sizeof(struct sockaddr)
  • 使用getaddrinfo() 以独立于协议的方式填充套接字地址结构。
  • "connect API" 你打算向这个 API 传递什么?
  • 感谢 getaddrinfo() API 如何以独立于协议的方式填充结构?
  • "怎么能...." : 你可能想看看here

标签: c sockets networking


【解决方案1】:

我们如何以编程方式将sockaddr_insockaddr_in6 转换为sockaddr 结构(填充其成员变量),然后使用sockaddr 结构调用connect?

类型转换不是转换。你没有转换任何东西。正如您提供的示例所示,您可以根据需要创建一个sockaddr_insockaddr_in6 实例,然后将其按原样传递给connect()。您只是对指向结构实例的 pointer 进行类型转换。在内部,connect() 将查看其输入addrsa_family 字段并将addr 类型转换回sockaddr_insockaddr_in6,以便根据需要访问数据字段。

我的目标是为 IPv6 和 IPv4 地址编写用户定义的连接 API。

如果你想编写与协议无关的代码,你可以这样做:

int doConnect(int family, const char *ip, u_short port)
{
    struct sockaddr_storage ss = {};
    socklen_t addrlen;

    switch (family)
    {
        case AF_INET:
        {
            struct sockaddr_in *addr = (struct sockaddr_in *) &ss;
            addr->sin_family = AF_INET;
            addr->sin_port = htons(port);
            inet_pton(AF_INET, ip, &addr->sin_addr);
            addrlen = sizeof(struct sockaddr_in);
            break;
        }

        case AF_INET6:
        {
            struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &ss;
            addr->sin6_family = AF_INET6;
            addr->sin6_port = htons(port);
            inet_pton(AF_INET6, ip, &addr->sin6_addr);
            addrlen = sizeof(struct sockaddr_in6);
            break;
        }

        default:
            return -1;
    }

    int s = socket(family, SOCK_STREAM_IPPROTO_TCP);
    if (s != -1)
    {
        if (connect(s, (struct sockaddr *) &ss, addrlen) < 0)
        {
            close(s);
            s = -1;
        }
    }

    return s;
}
int s = doConnect(AF_INET, "127.0.0.1", 5000);
int s = doConnect(AF_INET6, "::1", 5000);

但是,更好的解决方案是改用getaddrinfo(),让它根据实际解析的输入值为您分配正确的sockaddr数据,例如:

int doConnect(const char *ip, u_short port)
{
    struct addrinfo hints = {};
    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo *addr;

    char szPort[6];
    sprintf(szPort, "%hu", port);

    int s = -1;

    int ret = getaddrinfo(ip, szPort, &hints, &addr);
    if (ret == 0)
    {
        s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (s != -1)
        {
            if (connect(s, addr->ai_addr, addr->ai_addrlen) == -1)
            {
                close(s);
                s = -1;
            }
        }

        freeaddrinfo(addr);
    }

    return s;
}
int s = doConnect("127.0.0.1", 5000);
int s = doConnect("::1", 5000);

getaddrinfo() 的好处是您也可以将它用于服务器。只需在hints.ai_flags 字段中使用AI_PASSIVE 标志,然后使用生成的addrinfo 项目调用socket()bind()

【讨论】:

    【解决方案2】:

    我们如何以编程方式将sockaddr_insockaddr_in6 转换为sockaddr

    您不能,因为sockaddr_insockaddr_in6 都不能保证适合sockaddr 结构。

    A POSIX-compliant system will provide a sockaddr_storage type that is guaranteed to be large enough to hold any type of sockaddr structure:

    &lt;sys/socket.h&gt; 标头应定义 sockaddr_storage 结构,应为:

    • 足够大,可以容纳所有受支持的协议特定地址结构

    • 在适当的边界对齐,以便可以将指向它的指针转换为指向协议特定地址结构的指针并用于 在没有对齐问题的情况下访问这些结构的字段

    sockaddr_storage 结构应至少包括以下内容 成员:

    sa_family_t   ss_family
    

    当指向sockaddr_storage 结构的指针被转换为指针时 到sockaddr 结构,ss_family 字段 sockaddr_storage 结构应映射到 sa_family 字段 sockaddr 结构。当指针指向sockaddr_storage 结构被转换为指向特定协议地址的指针 结构,ss_family 字段应映射到该字段 sa_family_t 类型的结构,它标识 协议的地址族。

    【讨论】:

    • 那是什么?不能保证还是可以?
    • @user207421 我不知道“应定义 sockaddr_storage 结构,该结构应 [l] 大到足以容纳所有支持的协议特定地址结构”尚不清楚。
    猜你喜欢
    • 2013-09-07
    • 1970-01-01
    • 2012-07-28
    • 2021-12-12
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    相关资源
    最近更新 更多