我们在进行网络编程时,绝大部分情况都会涉及到 IP 协议,因此了解一下 IP 协议是很有必要的。不过由于 IP 协议本身不算简单,而我们更多的是使用 TCP/UDP 等上层协议进行网络编程,因此要必要了解一下 IP 地址。

IP 地址的组织形式

IP 地址的表示

IP 地址实际上是 32 bit 的数字(也就是4字节),不过由于二进制的可读性太差,所以计算机网络沿用了信息技术中的表示法,将 IP 地址按 8 bit 分成4组,每组用一个十进制数字来表示,数字之间用点做分隔,也就是所谓的点分十进制

dotted-decimal notation

比如,从上图可以看出 IP 地址 172.16.254.1 对应于二进制 10101100 00010000 11111110 00000001

因为计算机只能识别二进制,所以编程时需要将点分十进制转换成计算机可识别的二进制,这也就是为什么 Linux/Unix 需要 inet_aton inet_addr inet_ntoa 这三个函数进行 IP 地址的转换。

五类 IP 地址

虽说 IP 地址实际上是二进制数字,但它并不是像 0.0.0.00.0.0.10.0.0.2 ... 这样构造的,而是被划分为 ABCDE 五类。那么为什么要划分呢?主要是为了分配、识别和管理的方便1。比如:你看到 3.xxx.xxx.xxx 马上就能知道,这是 A 类地址,拥有这个地址的肯定是个大型机构(类似 省.市.区.家)。

five classes of IP

类别 范围 说明
A 类 0.0.0.0 - 127.255.255.255 用于规模非常大的网络,比如国家或者大公司
B 类 128.0.0.0 - 191.255.255.255 用于中等规模的网络,比如学校
C 类 192.0.0.0 - 223.255.255.255 通常用于小企业
D 类 224.0.0.0 - 239.255.255.255 用于多播
E 类 240.0.0.0 - 255.255.255.255 实验用2

根据接收端的数量,IP 地址又可以分成三种:unicast(特定主机)、broadcast(所有局域网中的主机)或multicast(一组主机)。

网段与子网掩码

因为广播和路由3的存在,需要将 IP 地址划分成不同的网段(试想全地球的人都呆在一个大房子里面会是多么不方便?),例如:ABC 类中的网络 ID 就指明了 IP 所在的网段。有时候为了联机玩魔兽方便的处理多个主机,我们需要将它们放到一个网段下,而且希望能够与其他主机隔离,这时候就可以通过设置子网掩码将固定网段进一步划分。

比如:C 类地址 192.168.137.1,子网掩码 255.255.255.240 就表示它位于 192.168.137.0 - 192.168.137.15 网段内,也就是同一个局域网下。

子网掩码长得很像 IP 地址,它实际上也是二进制数字,例如:255.255.255.240 对应 11111111 11111111 11111111 11110000。计算机最擅长做的是什么?二进制的运算。所以判断 IP 地址是不是位于某个网段是通过将 IP 地址与子网掩码做逻辑与来判断的。4

subnet calculattion

子网掩码可以是任意的 32 bits 数字,但是实际中无一例外是以全1开头全0结束。

相比使用 IP 地址和子网掩码表示网段,CIDR(Classless Inter-Domain Routing)表示法更为方便,可读性也更高。前面的例子用 CIDR 表示为 192.168.137.0/28。CIDR 表示法由三部分组成:IP 地址、斜杠、掩码前缀1的个数。网段的计算方法和子网掩码方式等价,除了斜杠后的数字表示子网掩码1开头的位数以外,并无区别。看下面这个例子就明白了。

192.168.100.0/22
11000000 10101000 01100100 00000000
11111111 11111111 11111100 00000000
192.168.100.0 - 192.168.103.255

特殊的 IP 地址

有些特殊的 IP 地址5我们在编程时经常遇到,这里列个表总结一下。

CIDR地址块 说明
0.0.0.06 本机的所有 IP 地址
127.0.0.0/8 环回地址
10.0.0.0/8 局域网
172.16.0.0/12 局域网
192.168.0.0/16 局域网
224.0.0.1 子网所有设备
255.255.255.255 广播地址
子网最后一个地址 广播地址
子网的第一个地址 网络识别码,代表整个子网,不能被分配

本机 IP

127.0.0.1

假设你需要写一个服务器。我们知道,监听套接字的时候得绑定一个 IP 地址,要不然客户端就不知道跟谁通信了。假设你在宿舍调试到一半要去上课,于是抱着电脑跑到教学楼,连上 WiFi,查看获取到的 IP ,再更改代码或者配置里面的 IP 地址,然后继续调试,下课回到寝室又得重复这个步骤...

环回地址解决了这个问题。它表示本机地址,发往环回地址的报文都不会出现在网络中(主机之外),而是被直接送到入口队列中(如果抓包不到,可能是这个原因)。所以 127.0.0.1 经常被用于调试或本机进程间通信。

0.0.0.0

回到前面那个例子,虽然将套接字绑定在 127.0.0.1 上能避免更换网络环境时改 IP 的麻烦,但是其他机器就访问不了服务器了。为了既能避免改 IP 的麻烦,又能让其他机器连接上来,我们可以将套接字绑定在 0.0.0.0 上。

0.0.0.0 代表了本机上所有的 IP 地址,什么意思呢?当你输入 ifconfig 命令的时候,你会看到一堆的 IP,包括:每个网卡的 IP、虚拟机的 IP、环回接口的 IP...而 0.0.0.0 就像黑洞,吞下了所有发往这些 IP 的数据。你从局域网访问服务器,它就是局域网的 IP,从本机访问它就是 127.0.0.1...

局域网地址

10.0.0.0 - 10.255.255.255172.16.0.0 - 172.31.255.255192.168.0.0 - 192.168.255.255 表示局域网 IP。位于局域网中的主机不能直接与公共网络通信,这些 IP 地址在局域网外也不能路由。但通过 NAT 可以做到前者。

如果你的服务器别人访问不了,检查一下 IP 地址是不是位于局域网中(也有可能是被网络管理员限制了)。

广播地址

如果你不知道什么是广播,分别 ping 一下 224.0.0.1255.255.255.255子网最后一个 IP 看看会发生什么。


参考文献


  1. 因为每个主机都有唯一的 IP,所以必须由一个组织—— Internet Network Information Center,来集中管理、分配。戳这里了解具体的分配情况。 

  2. IPv4 出现地址耗尽危机后有人提议使用 E 类地址,但是由于有些操作系统和路由器不支持 E 类地址,所以这部分地址并不能在实际中使用。 

  3. 因为路由器需要使用路由表保存所在网段的路由信息,而路由器的硬件资源有限只能保存部分路由信息,如果网段主机太多会导致路由表更新频繁,从而致使网络质量下降,所以需要划分网段。 

  4. 这里有些习题帮你巩固子网掩码的概念。 

  5. Wiki 上有更完整的特殊 IP 地址的列表。 

  6. 全零网络的详情见 Wikihnrainll的博客。