IPV4 套接口地址结构

“sockaddr_in”命名,定义在<netinet/in.h>头文件中。

查看帮助手册 命令man 7 ip 也可以查看到地址结构

struct sockaddr_in
{
    uint8_t sin_len;
    sa_family_t sin_fimily;       //2字节;固定值:AF_INET
    in_port_t sin_port;           //16 bit(2字节) TCP 或 UDP 端口号;使用网络字节序
    struct in_addr sin_addr;      //32 bit(4字节) IPv4地址 ; 使用网络字节序
    char sin_zero[8];             //8字节
};

struct in_addr                // IP 地址结构体
{
   in_addr_t s_addr;              //32 bit(4字节) IP地址
};

参数解读

sin_len:整个socketaddr_in 结构体的长度,为uint32_t ;(在4.3 BSD-Reno版本之前的第一个成员是sin_fimily,有一些平台tcp/ip的实现并没有这个字段)

sin_fimily:地址结构地址族;sin_family地址族对于IPV4协议来说必须是AF_INET。

为什么要有地址族?

因为socket在设计的时候,不仅仅能够用于TCP/IP协议,它还能用于其他的协议,比如说unix域协议,

所以我们必须指定地址家族,一旦地址家族指定为AF_INET,说明我们使用的是tcp/IP当中的IPv4协议。

如果指定为AF_INET6,则使用是是tcp/IP当中的IPv6协议。

sin_addr IPv4 地址是32位的,所以struct in_addr结构体当中,仅仅只有一个成员,这个成员就是无符号的32位的整数,并且是网络字节序

sin_port:端口,一般为uint16_t;端口是in_port_t 16位的无符号的整数,表示的最大数是65535

sin_addr:IPv4地址,一般为uint32_t

sin_zero[8]:保留字段,暂不使用,一般将其设置为0.

对于TCP IP协议来说 关键三个要素是sin_family,sin_port,sin_addr;编程时只需要关心上面三个字段。

IPv4两种使用方法:serv.sin_addr表示的是struct in_addr结构;而serv.sin_addr.in_addr表示的是一个uint32整数。

in_addr_t在头文件 <netinet/in.h> 中定义,等价于 unsigned long,长度为4个字节。也就是说,s_addr是一个整数,而IP地址是一个字符串,所以需要 inet_addr() 函数进行转换,例如:

unsigned long ip = inet_addr("127.0.0.1");
printf("%ld\n", ip);

运行结果:

16777343

通用套地址结构

当作为参数传递给任一个套接口函数时,套接口地址结构是通过指针传递的。但是通过指针来取得此参数的函数必须处理来自所支持的任何协议族的套接口地址结构。所以在<sys/socket.h>中定义一个通用地址接口:

struct sockaddr
{
    uint8_t sa_len;
    sa_fanily_t sa_family;
    char sa_data[14];
};

sin_len:整个socketaddr 结构体的长度。

sin_fimily:地址结构地址族。

sa_data: 由sin_fimily决定它的形式。

为什么要有通用的地址结构?

由于套接口不仅仅用于tcp/ip编程,它还能用于unix域协议进行编程,那么不同的协议他们的地址结构形式可能有不一样的地方,sockaddr_in 它仅仅用于IPv4地址结构

struct_sockaddr它可以用于任何协议的套接口编程,对比sockaddr_in中的

sa_data = sin_port(2字节) + sin_addr(4字节) + sin_zero[8](8字节) = 14字节

说明sockaddr_in 和 sockaddr的地址结构是兼容的。而我们进行TCP/IP编程的时候,实际上我们更多的是填充sockaddr_in这个地址,然后将这个地址强制转换成sockaddr通用的地址结构。

补充IPV6 套接口地址结构

可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体。

sockaddr_in6,用来保存 IPv6 地址,它的定义如下:

struct sockaddr_in6 { 
    sa_family_t sin6_family;  //(2)地址类型,取值为AF_INET6
    in_port_t sin6_port;  //(2)16位端口号
    uint32_t sin6_flowinfo;  //(4)IPv6流信息
    struct in6_addr sin6_addr;  //(4)具体的IPv6地址
    uint32_t sin6_scope_id;  //(4)接口范围ID
};

正是由于通用结构体 sockaddr 使用不便,才针对不同的地址类型定义了不同的结构体。

Last updated