• linux中的网络地址结构


    参考文章:http://blog.csdn.net/baobaoyeye/article/details/6175456

    看APUE中的网络IPC这章,其中讲到地址格式

    通用的地址结构sockaddr

    struct sockaddr{
        sa_family_t sa_familiy;              /* address family */
        char sa_data[];                          /* variable-length address */
        ...
        ...
    };
    

     实现可以自由地添加额外的成员并且定义sa_data成员的大小。

    在IPV4因特网域(AF_INET)中,套接字地址用结构sockaddr_in表示:

    struct in_addr {
        in_addr_t s_addr;           /* IPv4 address */
    };
    
    struct sockaddr_in {
        sa_family_t sin_family;          /* address family */
        in_port_t sin_port;                  /* port number */
        struct in_addr sin_addr;         /* IPv4 address */
    };
    

     IPv6因特网域(AF_INET6)套接字地址用如下结构sockaddr_in6表示:

    struct in6_addr {
        uint8_t s6_addr[16];              /* IPv6  address */
    };
    
    struct sockaddr_in6 {
        sa_family_t sin6_addr[16];    /* address family */
        in_port_t sin6_port;                /* port number */
        uint32_t sin6_flowinfo;           /* traffic class and flow info */
        struct in6_addr sin6_addr;     /* IPv6 address */
        uint32_t sin6_scope_id;          /* set of interfaces for scope */
    };
    

     以上是Single UNIX Specification必须的定义,每个实现可以自由的添加额外的字段。

    下面考察Linux中的实现,在Linux编程时,使用sockaddr结构时,要#include <sys/socket.h>,

    因此查看/urs/include/sys/socket.h内容,看到

    /* This operating system-specific header file defines the SOCK_*, PF_*,
       AF_*, MSG_*, SOL_*, and SO_* constants, and the `struct sockaddr',
       `struct msghdr', and `struct linger' types.  */
    #include <bits/socket.h>
    
    #ifdef __USE_BSD
    /* This is the 4.3 BSD `struct sockaddr' format, which is used as wire
       format in the grotty old 4.3 `talk' protocol.  */
    struct osockaddr
      {
        unsigned short int sa_family;
        unsigned char sa_data[14];
      };
    #endif
    

     对 #include <bits/socket.h>的注释中说了,sturct sockaddr实际上是定义在bits/socket.h中的

    (后面的osockaddr结构是4.3BSD中的sockaddr结构,可以看出SUS规定的和该结构完全一致,

    实际上,UNIX和Linux上的socket实现都是从BSD的socket实现演变过来的。)

    我们再查看bit/socket.h的内容,找到

    /* Get the definition of the macro to define the common sockaddr members.  */
    #include <bits/sockaddr.h>
    
    /* Structure describing a generic socket address.  */
    struct sockaddr
      {
        __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
        char sa_data[14];		/* Address data.  */
      };
    

     其中出现了一个宏__SOCKADDR_COMMON(),根据第1、2行到文件/usr/include/bits/sockaddr.h

    中去查找该宏,得到

    /* POSIX.1g specifies this type name for the `sa_family' member.  */
    typedef unsigned short int sa_family_t;
    
    /* This macro is used to declare the initial common members
       of the data types used for socket addresses, `struct sockaddr',
       `struct sockaddr_in', `struct sockaddr_un', etc.  */
    
    #define	__SOCKADDR_COMMON(sa_prefix) \
      sa_family_t sa_prefix##family
    

     第二行定义了sa_family_t类型,该类型在linux中就是无符号短整型,用来表示地址类型;

    第八行定义了__SOCKADDR_COMMON(sa_prefix)宏,其中出现了预定义宏##,该预定义宏的

    作用见另一篇博文();

    宏__SOCKADDR_COMMON(sa_prefix)定义了一个以sa_prefix和family合并起来为变量名的

    变量,该变量类型为sa_family_t。

    宏__SOCKADDR_COMMON(sa_prefix)展开得到

    sa_family_t sa_familiy;
    

     和SUS要求的一致。

    在考察linux中的sockaddr_in和sockadd_in6,使用这两个结构,应该#include <netinet/in.h>

    (apra/inet.h包含了netinet/in.h,使用#include <apra/inet.h>的话就不用包含后者了。)

    于是在/usr/include/netinet/in.h中查到到:

    /* Structure describing an Internet socket address.  */
    struct sockaddr_in
      {
        __SOCKADDR_COMMON (sin_);
        in_port_t sin_port;			/* Port number.  */
        struct in_addr sin_addr;		/* Internet address.  */
    
        /* Pad to size of `struct sockaddr'.  */
        unsigned char sin_zero[sizeof (struct sockaddr) -
    			   __SOCKADDR_COMMON_SIZE -
    			   sizeof (in_port_t) -
    			   sizeof (struct in_addr)];
      };
    
    /* Ditto, for IPv6.  */
    struct sockaddr_in6
      {
        __SOCKADDR_COMMON (sin6_);
        in_port_t sin6_port;	/* Transport layer port # */
        uint32_t sin6_flowinfo;	/* IPv6 flow information */
        struct in6_addr sin6_addr;	/* IPv6 address */
        uint32_t sin6_scope_id;	/* IPv6 scope-id */
      };
    

     其中sockaddr_in6和SUS要求的完全一样,sockaddr_in多了一个成员

    该成员中有一个宏定义__SOCKADDR_COMMON_SIZE,从字面意思看应该时__SOCKADDR_COMMON

    的大小,而前面看到宏__SOCKADDR_COMMON()实际上定义了一个sa_family_t类型(在linux中实现

    为无符号短整形)的变量

    前面在/usr/include/bits/sockaddr.h中看到该宏定义如下:

    #define __SOCKADDR_COMMON_SIZE	(sizeof (unsigned short int))
    

     验证了上述猜测。

    多出的一个成员起填充字段的作用,为了将sockaddr_in结构补全至和sockaddr结构的大小一致。

    这样,sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向

    sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,

    然后用进行类型转换就可以了。

    struct sockaddr_in *sinp;
    /* ai_addr为从某个函数返回sockaddr结构的指针 */
    sinp = (struct sockaddr_in *)ai_addr;
    

     在看看linux中的in_addr和in6_addr结构,在/usr/include/netinet/in.h中:

    /* Internet address.  */
    typedef uint32_t in_addr_t;
    struct in_addr
      {
        in_addr_t s_addr;
      };
    ...
    ...
    /* IPv6 address */
    struct in6_addr
      {
        union
          {
    	uint8_t	__u6_addr8[16];
    #if defined __USE_MISC || defined __USE_GNU
    	uint16_t __u6_addr16[8];
    	uint32_t __u6_addr32[4];
    #endif
          } __in6_u;
    #define s6_addr			__in6_u.__u6_addr8
    #if defined __USE_MISC || defined __USE_GNU
    # define s6_addr16		__in6_u.__u6_addr16
    # define s6_addr32		__in6_u.__u6_addr32
    #endif
      };
    

     ipv6地址结构中用到了共用体,其中三个变量都占128位(都是同一个IPv6地址,只是访问字节数不一样)。

  • 相关阅读:
    linux openresty 安装(图文死磕)
    openresty lua 调试 (图文死磕)
    windows openresty 死磕:安装和启动脚本
    SpringBoot SpringCloud 热部署 热加载 热调试
    SpringCloud 亿级流量 架构演进
    Zuul 详解,带视频
    Zuul Swagger 整合
    时间序列分解-STL分解法
    ISLR系列:(4.3)模型选择 PCR & PLS
    ISLR系列:(4.2)模型选择 Ridge Regression & the Lasso
  • 原文地址:https://www.cnblogs.com/zechen11/p/2268329.html
Copyright © 2020-2023  润新知