3 地址族与数据序列
IP 地址与端口号
网络地址
为了区分网络而设置的一部分 IP 地址,在寻找目标主机时,先访问网络地址,然后路由器接到数据以后,再浏览主机地址并把数据传给计算机
网络地址分类和主机的地址边界
只看首字节即可区分网络地址占用的字节数
- A 类地址首字节范围:0~127,网络地址占一个字节
- B 类:128~191,网络地址占两个字节
- C 类:192~223,网络地址占三个字节
等价于
- A 类地址首位以 0 开始
- B 类地址首位以 10 开始
- C 类地址首位以 110 开始
端口号
计算机中有网卡(NIC, Network Interface Card),通过 NIC 向计算机内传输数据用到 IP,操作系统负责将数据适当分配给套接字,这时候要利用端口号(i.e. 通过 NIC 接受的数据内有端口号),操作系统参考这个端口号把数据传输给相应端口的套接字
端口号由 16 位构成,范围 0~65535,但是 0~1023 是知名端口号(Well-known PORT)一般分配给特定的应用程序,所以应该分配这个范围以外的值。另外,虽然端口号不允许重复,但 TCP 和 UDP 套接字不会共用端口号,所以允许重复。例如某个 TCP 套接字已经使用的端口号,另外的 TCP 套接字无法使用,但是 UDP 套接字可以
总之,目标地址同时包含 IP 地址和端口号,只有这样,数据才能被传输到最终的应用程序
表示地址信息
表示 IPv4 的结构体
struct sockaddr_in
{
sa_family_t sin_family; // 地址族
uint16_t sin_port; // 16位 UCP/TCP 端口号
struct in_addr sin_addr; // 32位 IP 地址
char sin_zero[8]; // 不使用
};
struct in_addr
{
In_addr_t s_addr; // 32 位 IPv4 地址
};
sockaddr_in 成员
sin_family
每种协议使用的地址族不同,IPv4 使用 4 字节地址族,IPv6 使用 16 字节地址族
| 地址族 | 含义 |
|---|---|
| AF_INET | IPv4 网络协议使用的地址族 |
| AF_INET6 | IPv6 网络协议使用的地址族 |
| AF_LOCAL | 本地通信中采用的 UNIX 协议地址族 |
sin_port
该成员保存 16 位端口号,以网络字节序保存
sin_addr
32 位 IP 地址信息,也以网络字节序保存
sin_zero
是为了使 sockaddr_in 的大小和 sockaddr 保持一致而插入的成员,必须填充为 0,因为作为 bind 参数的时候,&serv_addr 需要强转成 struct sockaddr *,但是直接填充 sockaddr 结构体很麻烦,其结构如下:
bind 函数要求 sa_data 除了地址和端口号以外的信息全部填 0,继而就有了 sockaddr_in
网络字节序和地址变换
字节序与网络字节序
大端序:高位字节存放到低位地址,在一个数字中,左侧是高位,右侧是低位,但是对于地址而言,数字越小越是低位,所以一个数字 0x12345678 以大端序保存起来就是 0x12 0x34 0x56 0x78
小端序:高位字节存放到高位地址,举例 0x12345678 保存起来就是 0x78 0x56 0x34 0x12
每种 CPU 的数据保存形式不同,因此主机字节序(代表 CPU 保存方式)不同,目前 Intel 主要以小端序保存
但是字节序不同的计算机之间数据传输难以解析,因此在网络传输中统一约定方式,这种约定成为网络字节序(Network Byte Order),统一为大端序,就是说先把数据数组转化为大端序格式再传输
字节序转换
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
htons 中 h 代表主机字节序,n 代表网络字节序,s 指 short,l 指 long(在 Linux 中占4个字节)
除了向 sockaddr_in 结构体变量填充数据外,其他情况无需考虑字节序问题
网络地址初始化和分配
字符串转网络地址序整型
sockaddr_in 保存地址信息的成员为 32 位整型,因此为了分配 IP 地址,需要表示为 32 位整型
#include <arpa/inet.h>
in_addr_t inet_addr(const char * string);
// 传入字符串,成功返回 32 位大端序,失败返回 INADDR_NONE
还有一个功能完全相同的函数,区别在于直接设置 in_addr 结构体
还有功能相反的函数
把网络字节序 IP 地址值转化为字符串形式,调用该函数后应立即将字符串信息复制到其他内存空间,因为先前的空间为函数分配,可能被覆盖
网络地址初始化
对先前介绍函数的应用