⟅UNIX网络编程⟆⦔套接字地址结构

说在前面

  • 环境: WSL
  • 参考: UNIX网络编程

数据类型说明

数据类型说明头文件
int8_t带符号8位整数<sys/types.h>
uint8_t无符号8位整数<sys/types.h>
int16_t带符号16位整数<sys/types.h>
uint16_t无符号16位整数<sys/types.h>
int32_t带符号32位整数<sys/types.h>
uint32_t无符号32位整数<sys/types.h>
sa_family_t套接字地址结构的地址族<sys/socket.h>
socklen_t套接字地址结构的长度,一般为uint32_t<sys/socket.h>
in_addr_tIPv4地址,一般为uint32_t<netinet/in.h>
in_port_tTCP或UDP端口,一般为uint16_t<netinet/in.h>

套接字地址结构

  • IPv4套接字地址结构

    通常也被称为"网际套接字地址结构",以sockaddr_in命名。

    • 头文件
      #include <netinet/in.h>
      
    • 定义
      /*
      struct in_addr {
      	in_addr_t		s_addr;		// 32位,IPv4地址,网络序
      };
      */
      
      struct sockaddr_in {
      	uint8_t			sin_len;	// 8位,结构体长度(16字节)
      	sa_family_t		sin_family;	// 8位,AF_INET
      	in_port			sin_port;	// 16位,TCP或者UDP端口号,网络序
      	struct in_addr	sin_addr;	// 32位,IPv4地址,网络序
      	char			sin_zero[8];// unused
      };
      
    • sin_len字段
      unsigned short类型;为增加对OSI协议的支持而增加的,不是所有厂家都支持该字段,POSIX规范也未要求。该字段涉及了一些高级内容,暂不讨论。
    • POSIX规范只要求了sin_addr、sin_family以及sin_port字段。几乎所有的实现都增加了sin_zero字段(干啥用的?)。
    • in_addr_t数据类型必须是至少32位无符号整型in_port_t必须是至少16位无符号整型sa_family_t可以是任何无符号整型类型(在支持长度字段sin_len的实现中,sa_family_t通常为8位无符号整型;在不支持长度字段的实现中,通常为16位无符号整型)
    • 注意结构体in_addr与数据类型in_addr_t的区分。
  • 通用套接字地址结构

    • 头文件
      #include <sys/socket.h>
      
    • 定义
      struct sockaddr {
      	uint_8		sa_len;
      	sa_family_t	sa_family;		/* 支持的地址族, AF_xxx	*/
      	char		sa_data[14];	/* 14字节的协议数据	*/
      };
      
    • 在作为参数传递给各种套接字函数时,套接字地址结构总是以指针的形式来传递。但是,套接字函数必须处理来自所支持的任何协议族的套接字地址结构。(比如bind函数必须支持IPv4和IPv6,但是它们的套接字结构不相同,如果我们直接传递各自的指针是需要重载bind函数的)
    • 为了解决这个问题,定义了通用的套接字地址结构,用于不同协议套接字结构指针之间的类型强制转换
    • 由于不同实现中,地址结构长度字段可能没有,那么必须保证在该实现中所有协议的套接字地址结构都没有该字段;(sin_len、sa_len等)要么都有,要么都没有。
    • 疑问: 这个结构体的构造其实不太懂。为啥要填充成16字节?

      补充: 查找了bind等系统调用的代码,发现在其实现中,sockaddr类型的指针还是会通过sa_family转换为对应协议套接字结构体的指针。这样的话,即使协议套接字结构体长度大于十六,还是可以访问正确的数据。

  • IPv6套接字地址结构

    • 头文件
      #include <netinet/in.h>
      
    • 定义
      /*
      struct in6_addr {
      	uint8_t		s6_addr[16];		// 128位,IPv6地址,网络序
      };
      */
      #define SIN6_LEN
      struct sockaddr_in6 {
      	uint8_t			sin6_len;		// 8位,结构体长度(16字节)
      	sa_family_t		sin6_family;	// 8位,AF_INET6
      	in_port			sin6_port;		// 16位,传输层端口号,网络序
      	uint32_t		sin6_flowinfo;	// 流信息,未定义
      	struct in6_addr	sin6_addr;		// 128位,IPv6地址,网络序
      	uin32_t			sin6_scope_id;	// 标识范围ID
      };
      
    • 如果系统支持套接字地址结构中的长度(sin6_len)字段,那么SIN6_LEN必须定义。
    • 结构中的字段先后顺序做过编排,使得如果sockaddr_in6结构本身是64位对齐的,那么128位的sin6_addr也是64位对齐的(如下图)。
      在这里插入图片描述
      在一些64位处理机上,如果64位数据存储在某个64位边缘位置,那么对其访问将得到优化。例如访问sin6_addr字段时,若是按上述结构存储,仅需取两次;而如果不是对齐的,需要取三次。(如下图)
      在这里插入图片描述
    • sin6_flowinfo字段分成两部分:低序20位为流标,高序12位保留。该部分暂不讨论。
    • 对于具有范围的地址(scoped address),sin6_scope_id字段标识其范围。最常见的是链路局部地址(link-local address),由于一个网络节点(如主机)有多个网络接口(如网卡),但是网络接口的链路局部地址是相同的,而scope_id可以用以区分它们。
  • 新的通用套接字地址结构

    • 头文件
      #include <netinet/in.h>
      
    • 定义
      struct sockaddr_storage{
      	uint8_t		ss_len;		// 8位,结构体长度
      	ss_family_t	ss_family;	// 8位,支持的地址族, AF_xxx
      	/* 其他依赖于实现的字段,需要满足以下要求
      	 * 1)必须满足最苛刻的对齐要求
      	 * 2)必须足够大,可以容纳任何系统支持的套接字地址结构
      	*/
      };
      
    • ubuntu18.04中的实现
      // in (\usr\include\x86_64-linux-gnu\bits\sockaddr.h)
      /* Size of struct sockaddr_storage.  */
      #define _SS_SIZE 128
      
      // in (\usr\include\x86_64-linux-gnu\bits\socket.h)
      /* Structure large enough to hold any socket address (with the historical
         exception of AF_UNIX).  */
      #define __ss_aligntype	unsigned long int
      #define _SS_PADSIZE \
        (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
      
      struct sockaddr_storage
      {
          __SOCKADDR_COMMON (ss_);	/* Address family, etc.  */
          char __ss_padding[_SS_PADSIZE];
          __ss_aligntype __ss_align;	/* Force desired alignment.  */
      };
      
      可以看到在该实现中sockaddr_storage结构大小为128字节(这样就可以满足对齐?)
    • 除了ss_family和ss_len外,其他字段对用户来说是透明的(无法正确的访问其中的数据)。sockaddr_storage结构只有类型强制转换/复制为适合ss_family字段的套接字地址结构中才可以访问。
  • 套接字地址结构比较

在这里插入图片描述
对于可变长度的套接字地址结构,在作为参数传递时,需要同时传递结构的长度。

sockaddr_un结构本身不是变长的,但是路径名是可变的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值