簡體   English   中英

如何從sockaddr_in / sockaddr_in6結構創建sockaddr

[英]How to create sockaddr from sockaddr_in / sockaddr_in6 structure

以下是IPv6和IPv4 clinet代碼的示例代碼摘錄:

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。 由於它們都將結構轉換為sockaddr結構-以編程方式,我們如何將sockaddr_insockaddr_in6轉換為sockaddr結構(填充其成員變量),然后使用sockaddr sturcure調用connect?

以編程方式,我們如何將sockaddr_insockaddr_in6轉換為sockaddr結構(填充其成員變量),然后使用sockaddr結構調用connect?

類型轉換不是轉換 您沒有進行任何轉換。 就像您提供的示例所示,您可以根據需要創建sockaddr_insockaddr_in6實例,然后將其原樣傳遞給connect() 您只是在類型轉換指向結構實例的指針 在內部, 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()

以編程方式如何將sockaddr_insockaddr_in6轉換為sockaddr

您不能這樣做,因為sockaddr_insockaddr_in6都不能保證適合sockaddr結構。

符合POSIX標准的系統將提供一個sockaddr_storage 保證是足夠大以保存任何類型的類型sockaddr結構

<sys/socket.h>標頭應定義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並標識協議的地址族。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM