Linuxでよく見られるipアドレスおよびsocketアドレス表現方法

6086 ワード

最近、IPアドレスやsocketアドレスとの付き合いが多く、いくつかの一般的なソケットに遭遇しました.
  • sockaddr_in-ipv 4アドレス、number表記法4バイトでよい、例えば字面量表記法は「255.255.255.255.255.255」であり、デジタル表記法に移行し、ff.ff.ff.ff=>ffffffff
  • である.
  • sock_addr_in6--ipv 6アドレス、number表現法16バイトで
  • sockaddr--一般的なsocket APIのsocketアドレス汎用表現、16バイト
  • sockaddr_storage--ipv 6に互換性のあるsocketアドレスの汎用表現であり、28バイトの
  • IPv4 addressおよびSocket address for IPv4in_addrは4バイト、IPv 4のSocket addressは16バイト
    typedef __uint8_t       sa_family_t;
    typedef __uint16_t      in_port_t;
    typedef __uint32_t      in_addr_t;      /* base type for internet address */
    
    /*
     * Internet address (a structure for historical reasons)
     * Ipv4  ,  
     */
    struct in_addr {
        in_addr_t s_addr;
    };
    
    /*
     * Socket address, internet style.
     */
    struct sockaddr_in {
        __uint8_t       sin_len;
        sa_family_t     sin_family;
        in_port_t       sin_port;
        struct  in_addr sin_addr;
        char            sin_zero[8];
    };
    
    IPv6 addressおよびSocket address for IPv6、IPv 6のSocket addressは28バイト
    typedef __uint8_t       sa_family_t;
    typedef __uint16_t      in_port_t;
    
    /*
     * IPv6 address 16  128bit
     */
    typedef struct in6_addr {
        union {
            __uint8_t   __u6_addr8[16];
            __uint16_t  __u6_addr16[8];
            __uint32_t  __u6_addr32[4];
        } __u6_addr;                    /* 128-bit IP6 address */
    } in6_addr_t;
    
    #define s6_addr   __u6_addr.__u6_addr8
    
    #define INET6_ADDRSTRLEN 46
    
    /*
     * Socket address for IPv6
     */
    struct sockaddr_in6 {
        __uint8_t       sin6_len;       /* length of this struct(sa_family_t) */
        sa_family_t     sin6_family;    /* AF_INET6 (sa_family_t) */
        in_port_t       sin6_port;      /* Transport layer port # (in_port_t) */
        __uint32_t      sin6_flowinfo;  /* IP6 flow information */
        struct in6_addr sin6_addr;      /* IP6 address */
        __uint32_t      sin6_scope_id;  /* scope zone index */
    };
    
    

    様々なプロトコルを統一するために、socket対応インタフェースは、sockaddr(16 )およびsockaddr_storage(128 )の2つの汎用ソケットの構造体を定義し、sockaddr_storageは、sockaddr_in6(128 )のような比較的長いプロトコルに適合するために後に定義され、sockaddr_storageのような汎用ソケットを使用する必要がある場合、sockaddrに強く転換し、長さはsizeof(struct sockaddr_storage)である.
    図中、struct sockaddrは16バイト、struct sockaddr_storageは28バイト
    /*
     * [XSI] Structure used by kernel to store most addresses.
     */
    struct sockaddr {
        __uint8_t       sa_len;         /* total length */
        sa_family_t     sa_family;      /* [XSI] address family */
        char            sa_data[14];    /* [XSI] addr value (actually larger) */
    };
    
    /*
     * RFC 2553: protocol-independent placeholder for socket addresses
     */
    #define _SS_MAXSIZE     128
    #define _SS_ALIGNSIZE   (sizeof(__int64_t))
    #define _SS_PAD1SIZE    \
                (_SS_ALIGNSIZE - sizeof(__uint8_t) - sizeof(sa_family_t))
    #define _SS_PAD2SIZE    \
                (_SS_MAXSIZE - sizeof(__uint8_t) - sizeof(sa_family_t) - \
                                _SS_PAD1SIZE - _SS_ALIGNSIZE)
    
    /*
     * [XSI] sockaddr_storage
     */
    struct sockaddr_storage {
        __uint8_t       ss_len;                             /* address length */
        sa_family_t     ss_family;                          /* [XSI] address family */
        char            __ss_pad1[_SS_PAD1SIZE];
        __int64_t       __ss_align;                         /* force structure storage alignment */
        char            __ss_pad2[_SS_PAD2SIZE];
    };
    

    Linuxネットワークプログラミングでは、どのプロトコルを使用するも、同じsocket APIが使用されるが、各プロトコルがネットワークアドレスを表す構造体は異なり、例えばIPv 4がstruct sockaddr_in、IPv 6がstruct sockaddr_in6であるため、これらのネットワークアドレスを表す構造体をすべて共通の構造体に抽象化する必要があり、抽象化後の構造体はstruct sockaddrである.
    一般的な汎用socket構造体に関するAPIは、次のとおりです.
    /*application->kernel*/
    int bind   (int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
    
    int connect(int sockfd, const struct sockaddr *serv_addr,
                socklen_t addrlen);
    
    int sendto (int s, const void *msg, size_t len, int flags,
                const struct sockaddr *to, socklen_t tolen);
    
    /*kernel->application*/
    int  accept     (int s, struct sockaddr *addr, socklen_t *addrlen);
    
    int  recvfrom   (int s,  void  *buf, size_t len, int flags,
                     struct sockaddr *from, socklen_t *fromlen);
    
    int  getpeername(int s, struct sockaddr *name, socklen_t *namelen);
    int  getsockname(int s, struct sockaddr *name, socklen_t *namelen);
    

    では、なぜstruct sockaddr_storageという汎用構造体が現れたのでしょうか.
    共通のアドレスデータ構造として、その大きさはすべての具体的なプロトコルアドレス構造の大きさの最大値であるが、sizeof(struct sockaddr) = 16sizeof(struct sockaddr_in6) = 28struct sockaddrという共通のデータ構造holdはIPv 6に耐えられないことは明らかである.
    複数のプロトコルに関するネットワークプログラミングでは、struct sockaddr_storageという構造体で通信アドレスを表し、socket APIを呼び出す場合、struct sockaddr_storage強制タイプをstruct sockaddr_storageに変換する必要があり、アドレス長はstruct sockaddrである.次のようになります.
    struct sockaddr_storage addr;
    /*
     
    */
    sendto (s, *msg, len, flags, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
    

    IPv 4/IPv 6ハイブリッドプログラミングのDemo:
    struct sockaddr_storage addr;
    memset(&addr, 0, sizeof(struct sockaddr_storage));
    if (isIPv6 == TRUE)
    {
        struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)&addr;
        addr_v6->sin6_family = AF_INET6;
        addr_v6->sin6_port = 1234;
        inet_pton(AF_INET6, “2001:3211::1”, &(addr_v6->sin6_addr));
    }
    else
    {
        struct sockaddr_in *addr_v4 = (struct sockaddr_in *)&addr;
        addr_v4->sin_family = AF_INET;
        addr_v4->sin_port = 1234;
        inet_aton(“192.168.1.228”, &(addr_v4->sin_addr));
    }
    
    sendto(sock, buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
    

    参考文献


    https://www.cnblogs.com/tanghuimin0713/p/3425936.html
    https://blog.csdn.net/weixin_44874963/article/details/89395292