第三章.地址族与数据序列
3.1 分配给套接字的ip地址与端口号
IP是为了收发网络数据而分配给计算机的值;端口号是为了区分程序中创建的套接字的序号。
3.1.1 网络地址
IP地址分为两类:
- Ipv4:4字节地址
- IPv6:16字节地址
地址分为网络号和主机号:
3.1.2 网络地址分类与主机地址边界
只需通过IP地址的第一个字节即可判断网络地址占用的总字节数,因为我们根据IP地址的边界区分网络地址,如下所示:
-
A 类地址的首字节范围为:0~127
-
B 类地址的首字节范围为:128~191
-
C 类地址的首字节范围为:192~223 还有如下这种表示方式:
-
A 类地址的首位以 0 开始
-
B 类地址的前2位以 10 开始
-
C 类地址的前3位以 110 开始
3.1.3 用于区分套接字的端口号
端口号用于同一操作系统内区分不同的套接字而设立的。端口号由16位构成,范围为0~65536,但0~1023是知名端口,一般分配给特定应用程序。
虽然端口号不能重复,但是 TCP 套接字和 UDP 套接字不会共用端接口号,所以允许重复。
3.2 地址信息的表示
3.2.1 IPv4地址结构体
结构体的定义:
|
|
该结构体中提到的另一个结构体 in_addr 定义如下,它用来存放 32 位IP地址:
|
|
关于以上两个结构体的一些数据类型。
数据类型名称 | 数据类型说明 | 声明的头文件 |
---|---|---|
int 8_t | signed 8-bit int | sys/types.h |
uint8_t | unsigned 8-bit int (unsigned char) | sys/types.h |
int16_t | signed 16-bit int | sys/types.h |
uint16_t | unsigned 16-bit int (unsigned short) | sys/ |
int32_t | signed 32-bit int | sys/types.h |
uint32_t | unsigned 32-bit int (unsigned long) | sys/types.h |
sa_family_t | 地址族(address family) | sys/socket.h |
socklen_t | 长度(length of struct) | sys/socket.h |
in_addr_t | IP地址,声明为 uint_32_t | netinet/in.h |
in_port_t | 端口号,声明为 uint_16_t | netinet/in.h |
3.2.2 结构体 sockaddr_in 的成员分析
- 成员 sin_family 每种协议适用的地址族不同,比如,IPV4 使用 4 字节的地址族,IPV6 使用 16 字节的地址族。
地址族(Address Family) | 含义 |
---|---|
AF_INET | IPV4用的地址族 |
AF_INET6 | IPV6用的地址族 |
AF_LOCAL | 本地通信中采用的 Unix 协议的地址族 |
- 成员 sin_port
该成员保存 16 位端口号,重点在于,它以网络字节序保存。
- 成员 sin_addr
该成员保存 32 为IP地址信息,且也以网络字节序保存
- 成员 sin_zero
无特殊含义。只是为结构体 sockaddr_in 结构体变量地址值将以如下方式传递给 bind 函数。
在之前的代码中
|
|
此处 bind 第二个参数期望得到的是 sockaddr 结构体变量的地址值,包括地址族、端口号、IP地址等。
|
|
此结构体 sa_data 保存的地址信息中需要包含IP地址和端口号,剩余部分应该填充 0 ,但是这样对于包含地址的信息非常麻烦,所以出现了 sockaddr_in 结构体,然后强制转换成 sockaddr 类型,则生成符合 bind 条件的参数。
3.3 网络字节序和地址变换
3.3.1 字节序与网络字节序
CPU 保存数据的方式有两种,这意味着 CPU 解析数据的方式也有 2 种:
- 大端序(Big Endian):高位字节存放到低位地址
- 小端序(Little Endian):高位字节存放到高位地址
0x12345678:
- 大端序:0x12 0x34 0x56 0x78(按照计算机读取的顺序)
- 小端序:0x78 0x56 0x34 0x12(按照计算机的顺序进行读取)
网络传输数据时统一约定方式:大端序
3.3.2 字节序转换
转换字节序的函数:
|
|
通过函数名称掌握其功能,只需要了解:
htons 的 h 代表主机(host)字节序。 htons 的 n 代表网络(network)字节序。 s 代表 short l 代表 long
编写代码:
编译运行
|
|
结果:
Host ordered port: 0x1234
Network ordered port: 0x3412
Host ordered address: 0x12345678
Network ordered address: 0x78563412
这是在小端 CPU 的运行结果。大部分人会得到相同的结果,因为 Intel 和 AMD 的 CPU 都是小端序为标准。
3.4 网络地址的初始化和分配
3.4.1 将字符串信息转换为网络字节序的整数型
如何把类似于 201.211.214.36 转换为 4 字节的整数类型数据 ?
- 1.inet_addr函数
|
|
如果向该函数传递类似“211.214.107.99”的点分十进制格式的字符串,它会将其转换为32位整数型数据并返回。
具体示例: inet_addr.c
编译运行:
|
|
结果:
Network ordered integer addr: 0x4030201
Error occured!
- 2.inet_aton函数
inet_aton 函数与 inet_addr 函数在功能上完全相同,也是将字符串形式的IP地址转换成整数型的IP地址。只不过该函数用了 in_addr 结构体,如果传递in_addr结构体的变量地址值,函数会自动把结果填入该结构体变量。
|
|
具体示例: inet_aton.c
编译运行:
|
|
结果:
Network ordered integer addr: 0x4f7ce87f
- 还有一个函数,与 inet_aton() 正好相反,它可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式,函数原型如下:
|
|
该函数将通过参数传入的整数型IP地址转换为字符串格式并返回。**但要小心,返回值为 char 指针,返回字符串地址意味着字符串已经保存在内存空间,但是该函数未向程序员要求分配内存,而是再内部申请了内存保存了字符串。**也就是说调用了该函数候要立即把信息复制到其他内存空间。因此,若再次调用 inet_ntoa 函数,则有可能覆盖之前保存的字符串信息。
示例: inet_ntoa.c
编译运行:
|
|
结果:
Dotted-Decimal notation1: 1.2.3.4
Dotted-Decimal notation2: 1.1.1.1
Dotted-Decimal notation3: 1.2.3.4
3.4.2 网络地址初始化
常见的套接字创建的过程:
|
|
3.5 基于Windows端的实现
略
3.6 习题
最后修改于 2022-03-19
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。