当你在浏览器输入一个网址,或者在服务器配置文件中填写IP地址时,这些看似简单的数字串背后,其实是一套精心设计的地址系统。理解IP地址和子网掩码,是理解整个互联网如何运作的起点。

IP地址是什么

IP地址(Internet Protocol Address)是互联网协议地址的简称。它就像现实世界中的门牌号,用来唯一标识网络中的每一台设备。

IPv4地址是一个32位的二进制数。但直接用二进制表示太不直观了——谁愿意记住11000000101010000000000000000001这样的数字呢?于是人们将其分成4段,每段8位(一个字节),转换成十进制后用点分隔,这就是我们熟悉的"点分十进制"表示法。

上面的二进制数就变成了:192.168.0.1

每一段的取值范围是0-255,因为8位二进制能表示的最大值是$2^8-1 = 255$。

二进制与十进制的转换

理解IP地址,必须掌握二进制转换。一个8位二进制数,每一位的权重从右到左依次是:

$$2^7, 2^6, 2^5, 2^4, 2^3, 2^2, 2^1, 2^0$$

即:128, 64, 32, 16, 8, 4, 2, 1

以数字192为例:

  • $192 = 128 + 64 = 2^7 + 2^6$
  • 所以二进制是11000000

反过来,二进制10101000转十进制:

  • $128 + 32 + 8 = 168$

这个转换在网络计算中会反复用到,建议多练习几次。

IP地址的两部分:网络号与主机号

IP地址本质上由两部分组成:网络号(Network ID)和主机号(Host ID)。

  • 网络号:标识设备所在的网络,就像"XX省XX市XX区"
  • 主机号:标识该网络中的具体设备,就像"XX街道XX号"

举个例子:192.168.1.100

如果前三个数字是网络号,最后一个数字是主机号,那么:

  • 网络号:192.168.1 —— 表示这是一个名为"192.168.1"的网络
  • 主机号:100 —— 表示这个网络中编号为100的设备

问题来了:怎么知道前几个数字是网络号,后面几个是主机号?这就需要子网掩码来回答。

子网掩码的作用

子网掩码(Subnet Mask)是一个32位的二进制数,用来"遮罩"IP地址,区分出网络部分和主机部分。

子网掩码的规则很简单:用连续的1表示网络位,用连续的0表示主机位

最常见的子网掩码255.255.255.0,写成二进制是:

11111111.11111111.11111111.00000000

前24位是1,后8位是0。这意味着:IP地址的前24位是网络号,后8位是主机号

用与运算提取网络地址

把IP地址和子网掩码进行"按位与"(AND)运算,就能得到网络地址。

与运算的规则:

  • 1 AND 1 = 1
  • 1 AND 0 = 0
  • 0 AND 1 = 0
  • 0 AND 0 = 0

举例:IP地址192.168.1.100,子网掩码255.255.255.0

IP地址:    11000000.10101000.00000001.01100100  (192.168.1.100)
子网掩码:  11111111.11111111.11111111.00000000  (255.255.255.0)
------------------------------------------------
与运算结果:11000000.10101000.00000001.00000000  (192.168.1.0)

192.168.1.0就是网络地址,它代表整个网络本身,不能分配给任何设备。

IP地址的分类

在互联网早期,IP地址按首字节的数值范围分为五类:

类别 首字节范围 默认子网掩码 网络规模
A类 1-126 255.0.0.0 大型网络,约1677万台主机
B类 128-191 255.255.0.0 中型网络,约65534台主机
C类 192-223 255.255.255.0 小型网络,254台主机
D类 224-239 组播地址
E类 240-255 保留地址

这种"有类寻址"方式如今已很少使用,现代网络普遍采用CIDR(无类别域间路由),不再受限于A/B/C类的划分。

CIDR表示法

CIDR(Classless Inter-Domain Routing,无类别域间路由)是一种更灵活的表示方式,直接在IP地址后面加上斜杠和网络位数。

比如192.168.1.0/24

  • 24表示前24位是网络位
  • 等价于子网掩码255.255.255.0

再比如10.0.0.0/8

  • 8表示前8位是网络位
  • 等价于子网掩码255.0.0.0

CIDR的好处是可以表示任意大小的网络块,不局限于传统的A/B/C类。例如192.168.0.0/23表示一个包含512个地址的网络块,跨越了传统C类的边界。

公网IP与私网IP

并不是所有IP地址都能在互联网上直接使用。RFC 1918定义了三段私有IP地址范围,专门用于内部网络:

私有地址范围 CIDR表示 用途
10.0.0.0 - 10.255.255.255 10.0.0.0/8 大型企业内部网络
172.16.0.0 - 172.31.255.255 172.16.0.0/12 中型网络
192.168.0.0 - 192.168.255.255 192.168.0.0/16 家庭和小型办公网络

私有IP地址不能在公网上路由。当你用192.168.1.100访问互联网时,路由器会通过NAT(网络地址转换)把它转换成公网IP。

NAT的基本原理

NAT让多台设备共享一个公网IP。过程大致是:

  1. 内网设备(如192.168.1.100)发送数据包到互联网
  2. 路由器将源地址替换为公网IP,同时记录端口映射
  3. 收到回复后,根据端口映射将数据转发回内网设备

这就是为什么你家可能有好几台设备上网,但运营商只分配了一个公网IP。

特殊IP地址

有些IP地址有特殊用途:

127.0.0.0/8 —— 回环地址

  • 127.0.0.1是最常用的回环地址,也叫localhost
  • 发送到这个地址的数据包不会离开本机,直接返回
  • 常用于本地测试

0.0.0.0 —— 任意地址

  • 在服务器配置中表示"监听所有网络接口"
  • 路由中表示默认路由

255.255.255.255 —— 受限广播地址

  • 向本网络内所有设备广播

网络地址与广播地址

每个子网都有两个特殊地址不能分配给设备:

  • 网络地址:主机位全为0,如192.168.1.0/24中的192.168.1.0
  • 广播地址:主机位全为1,如192.168.1.0/24中的192.168.1.255

所以一个/24网络虽然有256个地址,但只有254个可用。

计算可用主机数

公式很简单:

$$可用主机数 = 2^{主机位数} - 2$$

减2就是去掉网络地址和广播地址。

192.168.1.0/24为例:

  • 主机位数 = $32 - 24 = 8$
  • 可用主机数 = $2^8 - 2 = 254$

子网划分入门

子网划分是把一个大网络切分成多个小网络。核心思想是"借位":从主机位借一些位来作为子网位。

假设有一个192.168.1.0/24网络,想分成4个子网:

  1. 需要借2位主机位($2^2 = 4$个子网)
  2. 新的子网掩码变成/26(24+2=26),即255.255.255.192
  3. 每个子网有$2^{6}-2=62$个可用地址

四个子网分别是:

  • 192.168.1.0/26:地址范围 192.168.1.1 - 192.168.1.62
  • 192.168.1.64/26:地址范围 192.168.1.65 - 192.168.1.126
  • 192.168.1.128/26:地址范围 192.168.1.129 - 192.168.1.190
  • 192.168.1.192/26:地址范围 192.168.1.193 - 192.168.1.254

默认网关

当设备要访问本网络之外的地址时,需要通过网关转发。默认网关通常配置为子网的第一个或最后一个可用地址。

例如在192.168.1.0/24网络中:

  • 默认网关常设为192.168.1.1192.168.1.254
  • 所有目的地不在本网段的数据包都发给网关
  • 网关(通常是路由器)负责转发到其他网络

常见配置示例

在Linux系统中查看IP配置:

ip addr show eth0
# 输出示例:
# inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0

静态配置IP地址:

# Ubuntu/Debian (netplan)
network:
  ethernets:
    eth0:
      addresses:
        - 192.168.1.100/24
      gateway4: 192.168.1.1
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]

在代码中判断两个IP是否在同一网段:

import ipaddress

def same_network(ip1, ip2, mask):
    net1 = ipaddress.ip_network(f"{ip1}/{mask}", strict=False)
    net2 = ipaddress.ip_network(f"{ip2}/{mask}", strict=False)
    return net1 == net2

print(same_network("192.168.1.100", "192.168.1.200", 24))  # True
print(same_network("192.168.1.100", "192.168.2.100", 24))  # False

总结

IP地址和子网掩码是网络通信的基石。32位的IPv4地址通过点分十进制表示,子网掩码决定了哪些位是网络号、哪些位是主机号。CIDR表示法提供了灵活的网络划分方式,而公私网地址的区分则让有限的IPv4地址能够支撑庞大的互联网。理解这些概念,是深入学习网络协议和系统配置的第一步。


参考资料: