第2天:网络的分类和拓扑结构
“网络就像城市的交通系统,有不同的规模和不同的连接方式。”
📚 今日目标
- 理解网络的分类(LAN、WAN、MAN)
- 掌握网络拓扑结构的类型
- 编写程序:扫描局域网设备
🌐 网络的分类
按地理范围分类
1. 局域网(LAN - Local Area Network)
定义:覆盖范围较小的网络,通常在一个建筑物或校园内。
生活中的例子:
- 你家里的WiFi网络
- 公司办公室的网络
- 网吧的网络
- 学校机房的网络
特点:
- 📏 范围:几米到几公里
- 🚀 速度:很快(100Mbps - 10Gbps)
- 💰 成本:低
- 👥 归属:个人或单位所有
类比理解:
局域网 = 你家的内部电话系统
只能在家里几个房间之间通话
2. 城域网(MAN - Metropolitan Area Network)
定义:覆盖一个城市范围的网络。
生活中的例子:
- 城市的有线电视网络
- 城市公共WiFi网络
- 城市监控网络
特点:
- 📏 范围:几十公里
- 🚀 速度:较快
- 💰 成本:中等
- 👥 归属:通常是运营商或政府
类比理解:
城域网 = 城市的公交系统
连接城市内的不同区域
3. 广域网(WAN - Wide Area Network)
定义:覆盖很大地理范围的网络,可以跨越国家和大洲。
生活中的例子:
- 互联网(Internet)
- 跨国公司的专用网络
- 电信运营商的骨干网
特点:
- 📏 范围:几百到几万公里
- 🚀 速度:相对较慢
- 💰 成本:高
- 👥 归属:运营商或大型组织
类比理解:
广域网 = 国际航空网络
连接世界各地的城市
对比总结
| 类型 | 覆盖范围 | 传输速度 | 延迟 | 例子 |
|---|---|---|---|---|
| LAN | 10m - 1km | 最快 | 最低 | 家庭WiFi |
| MAN | 10km - 100km | 较快 | 较低 | 城市网络 |
| WAN | 100km+ | 较慢 | 较高 | 互联网 |
🔷 网络拓扑结构
拓扑结构就是网络中各个设备的连接方式和布局形状。
1. 总线型拓扑(Bus Topology)
设备A --- 设备B --- 设备C --- 设备D
| |
(总线) (总线)
特点:
- ✅ 优点:布线简单,成本低
- ❌ 缺点:总线断了,整个网络瘫痪
- 📝 应用:早期以太网
类比:公交线路,所有站点都在一条线上
2. 星型拓扑(Star Topology)
设备B
|
设备A -- 交换机 -- 设备C
|
设备D
特点:
- ✅ 优点:一个设备故障不影响其他设备
- ✅ 管理方便,易于排错
- ❌ 缺点:中心设备(交换机)故障,整个网络瘫痪
- 📝 应用:现代最常用的拓扑结构
类比:太阳系,所有行星围绕太阳转
3. 环型拓扑(Ring Topology)
设备A --- 设备B
| |
设备D --- 设备C
特点:
- ✅ 优点:数据传输路径确定
- ❌ 缺点:一个设备故障影响整个网络
- 📝 应用:令牌环网络(已较少使用)
类比:接力赛跑,每个人传递给下一个人
4. 网状型拓扑(Mesh Topology)
设备A --- 设备B
| \ / |
| \ / |
| / \ |
| / \ |
设备D --- 设备C
特点:
- ✅ 优点:可靠性极高,有多条路径
- ❌ 缺点:布线复杂,成本高
- 📝 应用:军事网络、互联网骨干
类比:地铁网络,有多条线路可选
5. 树型拓扑(Tree Topology)
根交换机
/ \
交换机A 交换机B
/ \ / \
设备1 设备2 设备3 设备4
特点:
- ✅ 优点:易于扩展,层次清晰
- ❌ 缺点:根节点故障影响所有下级
- 📝 应用:企业网络、校园网
类比:公司组织架构
🔧 实战项目:局域网设备扫描器
让我们编写一个程序,扫描你所在局域网内的所有设备!
代码实现
# day02_lan_scanner.py
# 功能:扫描局域网内的活跃设备
import socket
import ipaddress
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_local_ip_and_subnet():
"""
获取本机IP地址和所在的子网
返回:
local_ip: 本机IP地址字符串
subnet: 子网字符串(如 "192.168.1.0/24")
"""
try:
# 创建socket连接到外部地址,获取本机IP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(("8.8.8.8", 80))
local_ip = sock.getsockname()[0]
sock.close()
# 从IP地址推断子网(假设是/24子网)
# 例如:192.168.1.100 -> 192.168.1.0/24
ip_parts = local_ip.split('.')
subnet = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.0/24"
return local_ip, subnet
except Exception as e:
print(f"获取本机IP失败:{e}")
return None, None
def check_host(ip, timeout=0.5):
"""
检查指定IP的主机是否在线
参数:
ip: 要检查的IP地址
timeout: 超时时间(秒)
返回:
如果主机在线,返回(ip, hostname)
如果主机不在线,返回None
工作原理:
尝试连接到目标IP的常用端口(80端口)
如果能连接,说明主机在线
"""
try:
# 尝试连接到80端口(HTTP端口)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
# 尝试连接
result = sock.connect_ex((str(ip), 80))
if result == 0:
# 连接成功,尝试获取主机名
try:
hostname = socket.gethostbyaddr(str(ip))[0]
except:
hostname = "未知主机名"
sock.close()
return (str(ip), hostname)
else:
sock.close()
return None
except:
return None
def ping_check(ip, timeout=0.5):
"""
使用更简单的方法检测主机是否在线
尝试多个常用端口
"""
common_ports = [80, 443, 22, 445, 139] # HTTP, HTTPS, SSH, SMB
for port in common_ports:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((str(ip), port))
sock.close()
if result == 0:
# 尝试获取主机名
try:
hostname = socket.gethostbyaddr(str(ip))[0]
except:
hostname = "未知主机名"
return (str(ip), hostname, port)
except:
continue
return None
def scan_network(subnet, max_workers=50):
"""
扫描整个子网
参数:
subnet: 子网地址(如 "192.168.1.0/24")
max_workers: 最大线程数
返回:
活跃主机列表
"""
# 解析子网,获取所有IP地址
network = ipaddress.ip_network(subnet, strict=False)
total_hosts = network.num_addresses - 2 # 减去网络地址和广播地址
print(f"🔍 开始扫描子网: {subnet}")
print(f"📊 总共需要扫描 {total_hosts} 个IP地址")
print(f"⏳ 请稍候...\n")
active_hosts = []
scanned = 0
# 使用线程池并发扫描
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交所有扫描任务
future_to_ip = {
executor.submit(ping_check, ip): ip
for ip in network.hosts() # 排除网络地址和广播地址
}
# 处理完成的任务
for future in as_completed(future_to_ip):
scanned += 1
result = future.result()
if result:
ip, hostname, port = result
active_hosts.append((ip, hostname, port))
print(f"✅ 发现设备: {ip:15} | {hostname:30} | 端口: {port}")
# 显示进度
if scanned % 10 == 0:
progress = (scanned / total_hosts) * 100
print(f" 进度: {progress:.1f}% ({scanned}/{total_hosts})", end='\r')
return active_hosts
def display_topology(active_hosts, local_ip):
"""
显示网络拓扑结构(简化版)
"""
print("\n" + "=" * 70)
print("网络拓扑结构(星型拓扑)".center(70))
print("=" * 70)
print()
print(" [路由器/交换机]")
print(" |")
print(" " + "-" * 40)
for ip, hostname, port in active_hosts:
marker = " (本机)" if ip == local_ip else ""
print(f" |-- [{ip}] {hostname}{marker}")
print()
print("=" * 70)
def main():
"""
主函数
"""
print("=" * 70)
print("局域网设备扫描器".center(70))
print("=" * 70)
print()
# 获取本机IP和子网
local_ip, subnet = get_local_ip_and_subnet()
if not local_ip:
print("❌ 无法获取本机IP地址,请检查网络连接!")
return
print(f"📍 本机IP: {local_ip}")
print(f"🌐 所在子网: {subnet}")
print()
# 扫描网络
active_hosts = scan_network(subnet)
print("\n" + "=" * 70)
print(f"✨ 扫描完成!共发现 {len(active_hosts)} 台活跃设备")
print("=" * 70)
# 显示详细信息
if active_hosts:
print("\n📋 活跃设备列表:")
print(f"{'IP地址':<15} | {'主机名':<30} | {'开放端口'}")
print("-" * 70)
for ip, hostname, port in active_hosts:
marker = " ⭐" if ip == local_ip else ""
print(f"{ip:<15} | {hostname:<30} | {port}{marker}")
# 显示拓扑结构
display_topology(active_hosts, local_ip)
# 分析网络类型
print("\n💡 网络分析:")
print(f" - 这是一个局域网(LAN)")
print(f" - 使用星型拓扑结构")
print(f" - 共有 {len(active_hosts)} 台设备连接到同一个交换机/路由器")
else:
print("\n⚠️ 未发现其他活跃设备")
if __name__ == "__main__":
main()
运行程序
cd code/day02
python day02_lan_scanner.py
预期输出
======================================================================
局域网设备扫描器
======================================================================
📍 本机IP: 192.168.1.100
🌐 所在子网: 192.168.1.0/24
🔍 开始扫描子网: 192.168.1.0/24
📊 总共需要扫描 254 个IP地址
⏳ 请稍候...
✅ 发现设备: 192.168.1.1 | router.home | 端口: 80
✅ 发现设备: 192.168.1.100 | linzhibin-MacBook | 端口: 80
✅ 发现设备: 192.168.1.105 | xiaomi-phone | 端口: 443
======================================================================
✨ 扫描完成!共发现 3 台活跃设备
======================================================================
📋 活跃设备列表:
IP地址 | 主机名 | 开放端口
----------------------------------------------------------------------
192.168.1.1 | router.home | 80
192.168.1.100 | linzhibin-MacBook | 80 ⭐
192.168.1.105 | xiaomi-phone | 443
======================================================================
网络拓扑结构(星型拓扑)
======================================================================
[路由器/交换机]
|
----------------------------------------
|-- [192.168.1.1] router.home
|-- [192.168.1.100] linzhibin-MacBook (本机)
|-- [192.168.1.105] xiaomi-phone
======================================================================
💡 网络分析:
- 这是一个局域网(LAN)
- 使用星型拓扑结构
- 共有 3 台设备连接到同一个交换机/路由器
🔍 代码详解
1. ipaddress模块
Python的ipaddress模块用于处理IP地址和网络:
import ipaddress
# 创建网络对象
network = ipaddress.ip_network("192.168.1.0/24")
# 获取网络中的所有主机IP
for ip in network.hosts():
print(ip) # 192.168.1.1, 192.168.1.2, ..., 192.168.1.254
2. 子网掩码 /24 是什么意思?
192.168.1.0/24 的含义:
/24 表示子网掩码有24个1:
11111111.11111111.11111111.00000000
即:255.255.255.0
可用IP范围:
192.168.1.1 到 192.168.1.254(共254个)
192.168.1.0 是网络地址
192.168.1.255 是广播地址
3. ThreadPoolExecutor 并发扫描
from concurrent.futures import ThreadPoolExecutor
# 创建线程池,最多50个线程同时工作
with ThreadPoolExecutor(max_workers=50) as executor:
# 提交任务
futures = [executor.submit(scan_ip, ip) for ip in ip_list]
# 等待完成
for future in as_completed(futures):
result = future.result()
为什么要用多线程?
- 单线程扫描254个IP需要很长时间
- 多线程可以同时扫描多个IP,大大提高效率
- 50个线程可以让扫描速度提升几十倍
4. connect_ex方法
result = sock.connect_ex((ip, port))
# 返回值:
# 0 = 连接成功
# 其他 = 连接失败(错误码)
这比普通的connect()方法更好,因为失败时不会抛出异常。
🎓 知识小结
今天学到了什么?
- ✅ 网络按范围分为LAN、MAN、WAN
- ✅ 家庭和办公室网络是局域网(LAN)
- ✅ 网络拓扑结构有5种:总线、星型、环型、网状、树型
- ✅ 现代网络最常用星型拓扑结构
- ✅ 子网的概念和表示方法
- ✅ 如何扫描局域网内的设备
重要概念
| 概念 | 解释 | 例子 |
|---|---|---|
| 局域网 | 小范围的网络 | 家庭WiFi |
| 星型拓扑 | 所有设备连接到中心节点 | 所有电脑连接到路由器 |
| 子网 | 一个网络的子集 | 192.168.1.0/24 |
| 主机 | 网络中的设备 | 你的电脑、手机 |
💪 练习题
练习1:识别拓扑类型
说出以下场景使用的是什么拓扑结构:
- 所有教室的电脑都连接到机房的交换机
- 地铁13号线各个站点的连接
- 互联网骨干网的连接方式
练习2:修改扫描器
修改程序,让它可以:
- 扫描自定义的子网(不是默认的/24)
- 显示每个设备的响应时间
- 保存扫描结果到文件
练习3:计算题
一个192.168.1.0/24的网络:
- 子网掩码是多少?
- 第一个可用IP是多少?
- 最后一个可用IP是多少?
- 总共有多少个可用IP?
练习4:绘制拓扑图
绘制你家里或公司的网络拓扑结构图,标注:
- 所有设备(电脑、手机、打印机等)
- 路由器/交换机
- 连接方式(有线/无线)
📚 扩展阅读
为什么星型拓扑最流行?
历史原因:
- 早期使用总线型拓扑(10BASE-2同轴电缆)
- 问题:一根线断了,所有设备都不能用
- 星型拓扑解决了这个问题
现代优势:
- 便于管理:可以在交换机上管理每个端口
- 易于排错:某个设备故障不影响其他设备
- 易于扩展:只需在交换机上增加端口
- 性能好:交换机可以智能转发数据
家庭网络的实际拓扑
互联网
|
光猫
|
路由器(WiFi)
|
|---- 电脑(有线)
|---- 手机(无线)
|---- 平板(无线)
|---- 智能电视(有线)
|---- 智能音箱(无线)
这是一个典型的星型拓扑!
企业网络的拓扑
核心交换机(根)
/ \
交换机A 交换机B
/ \ / \
电脑1 电脑2 电脑3 电脑4
这是一个树型拓扑(也叫分层星型拓扑)!
🎯 明天预告
第3天:OSI七层模型
我们将学习:
- 什么是OSI七层模型?
- 每一层做什么?
- 为什么要分层?
- 编写程序分析各层的作用
💡 今日金句:网络拓扑就像城市规划,不同的结构有不同的优缺点!
👉 上一天:认识计算机网络 | 返回目录 | 下一天:OSI七层模型