《UNIX网络编程 卷1:套接字联网API》学习笔记——套接字编程简介

概述

套接字地址结构可以在两个方向上传递:从进程到内核和从内核到进程。

地址转换函数在地址的文本表达和它们存放在套接字地址结构中的二进制值之间进行转换。

套接字地址结构

大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。
每个协议族都定义它自己的套接字地址结构。
这些结构的名字均以 sockaddr_ 开头,并以对应每个协议族的唯一后缀结尾。

IPv4 套接字地址结构

IPv4 套接字地址结构 也称为 “网际套接字地址结构”,它以 sockaddr_in 命名,定义在 <netinet/in.h> 头文件。
在这里插入图片描述
套接字地址结构的几点一般性的说明。

  • 长度字段 sin_len 是为增加对 OSI 协议的支持而随 4.3 BSD-Reno 添加的。
  • 即使有长度字段,也无须设置和检查它,除非涉及路由套接字。
  • POSIX 规范只需要这个结构中的3个字段:sin_family、sin_addr 和 sin_port 。
  • 给出了字段 s_addr、sin_family 和 sin_port 的POSIX数据类型。
    在这里插入图片描述
  • 还将遇到数据类型 u_char、u_short、u_int 和 u_long,它们都是无符号的。
  • IPv4地址和TCP或UDP端口号在套接字地址结构中总是以网络字节序来存储。
  • 32位IPv4地址存在两种不同的访问方法。
  • sin_zero 字段未曾使用,不过在填写这种套接字地址结构时,我们总是把该字段置为0。
  • 套接字地址结构仅在给定主机上使用:虽然结构中的某些字段(例如IP地址和端口号)用在不同主机之间的通信中,但是结构本身并不在主机之间传递。

通用套接字地址结构

当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递。

<sys/socket.h>头文件中定义一个通用的套接字地址结构。
在这里插入图片描述套接字函数被定义为以指向某个通用套接字地址结构的一个指针作为其参数之一,这正如 bind 函数的 ANSI C 函数原型所示:

int bind(int, struct sockaddr*, socklen_t);

这就要求对这些函数的任何调用都必须要将指向特定于协议的套接字地址结构的指针进行类型强制转换(casting),变成指向某个通用套接字地址结构的指针。

IPv6 套接字地址结构

IPv6 套接字地址结构在 <netinet/in.h> 头文件中定义。
在这里插入图片描述
上图中要注意以下几点。

  • 如果系统支持套接字地址结构中的长度字段,那么 SIN6_LEN 常值必须定义。
  • IPv6 的地址族是 AF_INET6,而 IPv4的地址族是 AF_INET。
  • 结构中字段的先后顺序做过编排,使得如果 sockaddr_in6 结构本身是64位对齐的,那么128位的 sin6_addr 字段也是64位对齐的。
  • sin6_flowinfo 字段分为两个字段:
    低位20位是流标(flow label);
    高序12位保留。
    -对于具备范围的地址(scoped address), sin6_scope_id 字段标识其范围(scope),最常见的是链路局部地址(link-local address)的接口索引(interface index)。

新的通用套接字地址结构

sockaddr_storage 结构在 <netinet/in.h> 头文件中定义。

在这里插入图片描述
sockaddr_storage 类型提供的通用套接字地址结构相比 sockaddr 存在以下两点差别。

  • (1)如果系统支持的任何套接字地址结构有对齐需要,那么 sockaddr_storage 能够满足最苛刻的对齐要求。
  • (2)sockaddr_storage 足够大,能够容纳系统支持的任何套接字地址结构。

套接字地址结构的比较

在这里插入图片描述

值-结果参数

传递方式取决于该结构的传递方向:是从进程到内核,还是从内核到进程。

  • (1)从进程到内核传递套接字地址结构的函数有3个:bind、connect 和 sendto 。这些函数的一个参数是指向某个套接字地址结构的指针,另一个参数是该结构的整数大小。
    在这里插入图片描述

  • (2)从内核到进程传递套接字地址结构的函数有4个:accept、recvfrom、getsockname 和 getpeername 。
    这4个函数的其中两个参数是指向某个指向某个套接字地址结构的指针和指向表示该结构大小的整数数量的指针。
    在这里插入图片描述
    当使用值-结果参数作为套接字地址结构的长度时,如果套接字地址结构是固定长度的,那么从内核返回的值总是那个固定长度。
    对于可变长度的套接字地址结构,返回值可能小于该结构的最大长度。

值-结果参数最常见的例子是所返回套接字地址结构的长度。

其他值-结果参数:

  • select 函数中间的3个参数;
  • getsockopt 函数的长度参数;
  • 使用 recvmsg 函数时,msghdr 结构中的 msg_namelen 和 msg_controllen 字段;
  • ifconf 结构中的 ifc_len 字段;
  • sysctl 函数两个长度参数中的第一个。

字节排序函数

考虑一个16位整数,它由2个字节组成。

内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端字节序;另一种方法是将高序字节存储在起始地址,这称为大端字节序。

在这里插入图片描述
在图中,在顶部标明内存地址增长的方向为从右到左,在底部标明内存地址增长的方向为从左到右。还标明最高有效为(MSB)是这个16位值最左边一位,最低有效位(LSB)是这个16位值最右边一位。

把某个给定系统所用的字节序称为主机字节序

网络协议必须指定一个网络字节序

发送协议栈和接收协议栈必须就这些多字节字段各个字节的传送顺序达成一致。
网际协议使用大端字节序来传送这些多字节整数。

因特网标准中另一个重要的约定是位序。
在许多作为因特网标准的RFC文档中,可以看到类似如下的分组“图”示:
在这里插入图片描述
它表示按照在线缆上出现的顺序排列的4个字节(32个位),最左边的位是最早出现的最高有效位。
注意位序的编号从0开始,分配给最高有效位的编号为0。

字节操纵函数

操纵多字节字段的函数有两组,它们既不对数据作解释,也不假设数据是以空字符结束的 C 字符串。

以空字符结尾的C字符串是由在<string.h> 头文件中定义、名字以 str (表示字符串)开头的函数处理的。

名字以 b (表示字节)开头的第一组函数起源于 4.4BSD,几乎所有现今支持套接字函数的系统仍然提供它们。
名字以 mem (表示内存)开头的第二组函数起源于 ANSI C 标准,支持 ANSI C 函数库的所有系统都提供它们。

Berkeley 的函数:
在这里插入图片描述
bzero 把目标字节串中指定数目的字节置为0。
bcopy 将指定数目的字节从源字节串移到目标字节串。
bcmp 比较两个任意的字节串,若相同则返回值为0,否则返回值为非0。

ANSI C 函数:
在这里插入图片描述
memset 把目标字节串指定数目的字节置为值 c 。
memcpy 类似 bcopy ,不过两个指针参数的顺序是相反。

当源字节串于目标字节串重叠时,bcopy 能够正确处理,但是memcpy 的操作结果却不可知。这种情形下必须改用 ANSI C 的 memmove 函数。

memcmp 比较两个任意的字节串,若相同则返回0,否则返回一个非0值,是大于0还是小于0则取决于第一个不等的字节;如果 ptr1 所指字节串中的这个字节大于 ptr2 所指字节中的对应字节,那么大于0,否则小于0。

inet_aton、inet_addr 和 inet_ntoa 函数

两组地址转换函数。
它们在 ASCII 字符串于网络字节序的二进制值(这是存放在套接字地址结构中的值)之间转换网际地址。

  • (1)inet_aton、inet_addr 和 inet_ntoa 在点分十进制数串(如 “206.168.112.96”)与它长度为32位的网络字节序二进制值间转换 IPv4 地址。
  • (2)两个较新的函数 inet_pton 和 inet_ntop 对于 IPv4 地址和 IPv6 地址都适用。

在这里插入图片描述

  • 第一个函数 inet_aton 将 strptr 所指C字符串转换成一个32位的网络字节序二进制值,并通过指针 addrptr 来存储。若成功则返回1,否则返回0。

  • inet_addr 进行相同的转换,返回值为32位的网络字节序二进制值。 点分十进制数串 255.255.255.255 不能由该函数处理,因为它的二进制值被用来指示该函数失败。
    目前已被废弃,新的代码应该改用 inet_aton函数。

  • inet_ntoa 函数将一个32位的网络字节序二进制 IPv4地址转换成相应的点分十进制数串。由该函数的返回值所指向的字符串驻留在静态内存中。

inet_pton 和 inet_ntop 函数

这两个函数是随 IPv6出现的新函数,对于IPv4 地址和 IPv6 地址都适用。

函数名中 p 和 n 分别代表表达和数值。

地址的表达格式通常是ASCII字符,数值格式则是存放到套接字地址结构中的二进制值。

在这里插入图片描述这两个函数的 family 参数既可以是 AF_INET,也可以是 AF_INET6 。

第一个函数尝试转换由 strptr 指针所指的字符串,并通过 addrptr 指针存放二进制结果。若成功则返回值为1,否则如果对所指定的 family 而言输入的字符串不是有效的表达格式,那么返回值为0。
inet_ntop 进行相反的转换,从数值格式(addrptr)转换到表达格式(strptr)。len 参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。
inet_ntop 函数的 strptr 参数不可以是一个空指针。

在这里插入图片描述

学习参考资料:

《UNIX网络编程 卷1:套接字联网API》 第3版

版权声明:本文为weixin_45243288原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。