简介:本资源是《计算机网络》经典教材配套习题的完整参考答案,面向高校计算机、通信及相关专业本科生与考研备考学生,聚焦网络基础核心概念的理解与解题训练。内容覆盖第一章“概述”全部课后习题(共20余道),系统解析连通性与共享本质、分组交换原理、三大交换技术对比、Internet发展脉络、标准制定流程、internet/Internet辨析、网络分类与性能指标计算等关键知识点,尤其包含多道典型时延分析与传输效率计算题的详细推导过程,助力夯实理论基础、提升解题规范性与计算能力。资源为单个PDF文件,大小4.06MB,排版清晰、公式完整、答案严谨,便于打印学习或碎片化查阅。目前已有84人下载学习,适合作为课堂补充、自学检验与期末复习的重要参考资料。
1. 这不是“答案速查表”,而是《计算机网络》习题背后的思维训练场:为什么照抄PDF会越学越迷,而手推一遍TCP三次握手能真正看懂Wireshark抓包?
你手里的《计算机网络》习题答案.pdf,大概率是某届学生整理的扫描件、OCR识别后带错别字的文本,或是从论坛扒下来的零散截图拼凑——它能帮你交作业,但几乎必然让你在期末考前夜对着“拥塞窗口cwnd=4”发呆:这数字怎么来的?为什么不是5?BGP路由反射器那道题,答案写“防止环路”,可你连AS_PATH怎么拼接都画不出来。这不是你笨,是PDF把“推导过程”这个最硬核的肌肉给切掉了。真正的网络能力,长在你手动画出滑动窗口示意图、亲手用scapy构造SYN包并观察ACK序列号变化、把RIP跳数限制15条规则反向推演成环路计数器的那一刻。这篇笔记不提供任何PDF下载链接,也不教你“如何找答案”,而是带你用真实工具复现教材里每一道经典习题的底层逻辑:用Python重现实验环境,用Wireshark验证理论假设,用Linux内核参数调试RTO计算偏差。适合正在啃谢希仁第8版、Kurose第7版,或准备CCNA/软考网工的实践派学习者——答案只是路标,而你要亲手铺出那条路。
2. 把抽象协议变成可触摸的代码:用Scapy+Linux构建本地网络实验沙盒
教材里“TCP连接建立需三次握手”这句话背后,是SYN、SYN-ACK、ACK三个数据包的时序、标志位、序列号、确认号、窗口大小、MSS选项的精密配合。光看文字描述,永远不如亲手构造一个SYN包并被内核拒绝来得震撼。我们不用虚拟机、不装GNS3,只用本机Linux(Ubuntu 22.04+/CentOS 8+)和Scapy,搭建最小可行实验环境。
2.1 环境初始化:禁用内核自动响应,夺回数据包控制权
Linux内核默认会对收到的SYN包自动回复SYN-ACK,这会干扰我们手动构造握手流程。必须先关闭该行为:
# 关闭本机对SYN的自动响应(关键!否则Scapy发的SYN会被内核抢答) sudo sysctl -w net.ipv4.tcp_syncookies=0 sudo sysctl -w net.ipv4.conf.all.rp_filter=0 sudo sysctl -w net.ipv4.conf.eth0.rp_filter=0 # 替换eth0为你的实际网卡名 # 防火墙放行自定义端口(如我们用8000端口做测试) sudo ufw allow 8000提示:
rp_filter=0是为了绕过反向路径过滤,避免Scapy构造的源IP被丢弃;tcp_syncookies=0确保内核不启用SYN Cookie机制,让我们的手工SYN包能被正常处理而非被拦截。
2.2 构造并发送SYN包:看清三次握手的第一帧
以教材经典习题“主机A向B发起连接,初始序列号ISN_A=1000,B的ISN_B=2000”为例,用Scapy构造SYN包:
from scapy.all import * # 定义参数(严格对应习题条件) src_ip = "192.168.1.100" # A的IP(本机) dst_ip = "192.168.1.200" # B的IP(另一台机器或本机别名) src_port = 50000 # A的源端口 dst_port = 8000 # B的监听端口 isn_a = 1000 # A的初始序列号 # 构造IP层 + TCP层(SYN标志置位,ACK=0,序列号=ISN_A,窗口大小=65535) ip_pkt = IP(src=src_ip, dst=dst_ip) tcp_pkt = TCP(sport=src_port, dport=dst_port, flags="S", seq=isn_a, window=65535, options=[('MSS', 1460)]) # 发送并捕获响应(timeout=2秒,verbose=0减少干扰输出) response = sr1(ip_pkt/tcp_pkt, timeout=2, verbose=0) if response and TCP in response and response[TCP].flags & 0x12: # SYN-ACK标志位为0x12(SYN+ACK) print(f"[✓] 收到SYN-ACK:seq={response[TCP].seq}, ack={response[TCP].ack}") # 记录B的ISN_B(即SYN-ACK的seq字段),用于下一步构造ACK isn_b = response[TCP].seq else: print("[✗] 未收到有效SYN-ACK,请检查目标端口是否监听、防火墙设置")这段代码在做什么?
flags="S"明确指定SYN标志,而非依赖Scapy默认行为;options=[('MSS', 1460)]模拟教材中常提的MSS协商过程,1460是标准以太网MTU减去IP/TCP头部;sr1()发送并等待第一个响应包,避免收到无关ICMP;response[TCP].flags & 0x12用位运算精准判断是否为SYN-ACK(0x12 = 0b00010010),比字符串匹配更可靠。
2.3 完成三次握手:用ACK确认B的ISN,并验证状态同步
收到SYN-ACK后,A需发送ACK包,其确认号应为ISN_B + 1,序列号为ISN_A + 1:
if 'isn_b' in locals(): # 确保上一步成功获取ISN_B # 构造ACK包:seq=ISN_A+1, ack=ISN_B+1, flags="A" ack_pkt = IP(src=src_ip, dst=dst_ip) / \ TCP(sport=src_port, dport=dst_port, flags="A", seq=isn_a + 1, ack=isn_b + 1, window=65535) # 发送ACK(无需等待响应,三次握手完成) send(ack_pkt, verbose=0) print(f"[✓] ACK发送成功:seq={isn_a+1}, ack={isn_b+1}") # 验证:此时用netstat应看到连接处于ESTABLISHED状态 # (需在目标B端运行:sudo nc -lvp 8000)为什么这步不能省?
教材习题常问“第三次握手的确认号是多少?”,答案不是死记“ISN_B+1”,而是理解:确认号是对对方下一个期望字节的承诺。B发SYN-ACK时,其序列号是ISN_B,表示“我从ISN_B开始编号”,所以A必须确认ISN_B+1,表明“我收到了你的SYN,期待你从ISN_B+1开始发数据”。手敲这行ack=isn_b + 1,比背10遍定义更刻骨。
3. 从Wireshark抓包反向验证:让教材图示在真实流量中“活”过来
纸上谈兵终觉浅。把Scapy构造的包放到真实网络中,用Wireshark抓取并逐帧分析,才能把教材里的方框箭头变成你眼中的字节流。这不是“看热闹”,而是用工具解构协议细节。
3.1 抓包配置:过滤特定连接,聚焦关键字段
启动Wireshark前,先在终端运行Scapy脚本触发一次完整握手,然后在Wireshark中设置显示过滤器:
ip.src == 192.168.1.100 && ip.dst == 192.168.1.200 && tcp.port == 8000注意:务必用
ip.src/ip.dst而非tcp.stream eq X,因为后者依赖Wireshark自动关联,而手工构造的包可能因时间戳或窗口异常被误判为不同流。
3.2 教材习题对照表:Wireshark字段与课本术语映射
| Wireshark显示字段 | 教材术语 | 习题常见考点 | 实操验证方法 |
|---|---|---|---|
TCP Seq number(Relative) | 序列号(Seq) | “第三次握手的Seq值是多少?” →ISN_A+1 | 查看第三帧ACK包的Seq列 |
TCP Ack number(Relative) | 确认号(Ack) | “SYN-ACK包的Ack值?” →ISN_A+1 | 查看第二帧SYN-ACK的Ack列 |
TCP Flags [S]/[SA]/[A] | SYN / SYN-ACK / ACK标志 | “哪一帧携带SYN和ACK?” → 第二帧 | 直接看Flags列的图标或十六进制值 |
TCP Window size value | 接收窗口(rwnd) | “窗口大小如何影响吞吐量?” | 修改Scapy中window=参数,对比抓包 |
TCP Options > MSS | 最大报文段长度(MSS) | “MSS=1460意味着什么?” | 展开Options,看Value是否为0x05b4 |
关键技巧:右键点击任意TCP包 → “Protocol Preferences” → 勾选“Relative sequence numbers”,这样Wireshark自动将初始Seq设为0,与教材习题中“相对序列号”的表述完全一致,避免绝对值带来的混淆。
3.3 动态验证拥塞控制:用tc命令模拟丢包,观察慢启动行为
教材中“慢启动门限ssthresh”、“拥塞窗口cwnd”等概念,仅靠公式cwnd = min(cwnd, ssthresh)无法建立直觉。我们用Linuxtc(traffic control)命令人为制造丢包,观察cwnd如何响应:
# 在B端(接收方)模拟10%丢包率(模拟网络拥塞) sudo tc qdisc add dev eth0 root netem loss 10% # 运行一个持续发送HTTP请求的脚本(触发TCP传输) while true; do curl -s http://192.168.1.200:8000/test > /dev/null; sleep 0.1; done # 在A端用ss命令实时查看cwnd变化(需root权限) watch -n 1 'ss -i | grep :8000'你会看到什么?
- 初始几秒,
cwnd从10递增至20、40、80(典型的慢启动指数增长); - 一旦出现丢包,
cwnd骤降至1,ssthresh设为原cwnd一半(如原为80,则ssthresh=40); - 后续进入拥塞避免阶段,cwnd线性增长(每RTT+1)。
这比背诵“慢启动:cwnd=1→2→4→8…”生动一万倍。血泪经验:很多同学以为cwnd是内核“固定算法”,其实它是根据真实丢包反馈动态调整的——没有丢包,就没有拥塞控制。
4. 避坑指南:那些让《计算机网络》习题答案PDF失效的5个致命细节
照抄PDF答案翻车,往往不是因为答案错,而是因为题目隐含条件被忽略、环境差异被无视、或者协议版本已迭代。以下是我在带学生debug时高频遇到的5类坑,每一条都附真实案例和解决路径。
4.1 现象:习题答案写“RIP最大跳数为15”,但Wireshark抓包显示跳数为16
原因:RIP v1和v2对跳数的定义不同。RIP v1将16定义为“不可达”,但某些实现(尤其旧版Quagga)在发送时会把15跳路由标记为16跳宣告;而教材习题多基于RFC 1058(RIP v1),未说明版本差异。
解决:在Wireshark中右键RIP包 → “Decode As” → 强制选择“RIPv1”或“RIPv2”,对比跳数值;用tcpdump -i eth0 udp port 520 -X查看原始UDP载荷,确认第5字节(跳数字段)是否为0x10(16)。
4.2 现象:Scapy构造的ICMP ping包,目标主机无响应,但系统自带ping命令正常
原因:Linux内核默认禁止非root用户发送原始ICMP包(需CAP_NET_RAW权限),而Scapy默认不提升权限;同时,目标主机可能启用了icmp_echo_ignore_all=1(忽略所有ping)。
解决:
- 运行Scapy前加
sudo; - 在目标主机检查:
sysctl net.ipv4.icmp_echo_ignore_all,若为1则临时设为0:sudo sysctl -w net.ipv4.icmp_echo_ignore_all=0。
4.3 现象:BGP路由更新报文中AS_PATH属性为空,与教材图示不符
原因:现代BGP实现(如FRRouting)默认启用AS_PATH prepending(前置),且当路由来自本AS时,AS_PATH可能为空或仅含本AS号;而教材图示多基于RFC 4271早期实现,未考虑联盟(Confederation)或路由反射器(Route Reflector)场景。
解决:在BGP配置中显式设置no bgp default ipv4-unicast(禁用默认地址族),并用show ip bgp neighbors X.X.X.X advertised-routes查看实际发送的AS_PATH;用Wireshark过滤bgp.type == 2(UPDATE报文),展开BGP Attributes查看AS_PATH TLV。
4.4 现象:用netstat -s | grep -i "tcp"查看统计,发现“retransmit”数量远高于预期
原因:netstat -s统计的是内核TCP栈全局重传次数,包含所有连接的累计值,而非单次连接;且Linux 5.10+内核将部分重传归类为“fast retransmit”而非“retransmit”,导致数值失真。
解决:针对单连接诊断,改用ss -i(显示单连接详细信息)或cat /proc/net/snmp | grep Tcp查看实时统计;更精准的方法是用eBPF工具(如tcpretrans)跟踪特定socket的重传事件。
4.5 现象:DHCP Discover包发出后,Wireshark显示Destination MAC为ff:ff:ff:ff:ff:ff,但教材习题答案写“目的MAC为全0”
原因:教材(尤其老版)常将DHCP Discover的以太网目的MAC误写为00:00:00:00:00:00,这是对“未获知服务器MAC”的概念混淆——实际协议规定必须使用广播MACff:ff:ff:ff:ff:ff,因为客户端此时根本不知道服务器在哪。
解决:直接查阅RFC 2131 Section 4.1:“The client MUST use the limited broadcast address (255.255.255.255) as the IP destination address... and the hardware broadcast address as the link-layer destination address.” —— 以太网层必须用广播MAC,这是铁律。
5. 把习题答案PDF变成你的私人知识引擎:用Jupyter Notebook构建可执行、可验证、可追溯的学习工作区
PDF的答案是静态快照,而网络协议是动态过程。我坚持用Jupyter Notebook重构所有《计算机网络》习题,不是为了炫技,而是让每个答案都成为可运行、可调试、可溯源的活文档。下面是我压箱底的模板结构,已验证在谢希仁第8版全部课后题中复用。
5.1 Notebook核心模块设计:四象限驱动学习闭环
| 模块 | 内容说明 | 工具链 | 价值点 |
|---|---|---|---|
| ① 习题原文重述 | 复制教材原题(含图示编号),标注章节页码(如P127 T3.5) | Markdown + LaTeX公式 | 避免脱离上下文,明确考察知识点 |
| ② 手动推导草稿 | 用Mermaid绘制时序图/状态机(如TCP状态转换)、手算公式(如CSMA/CD冲突检测时间) | Mermaid + Python符号计算库 | 强制暴露思维断点,发现“我以为懂了其实没懂” |
| ③ 可执行验证 | Scapy/Wireshark/Python代码块,输入即运行,输出即结果(如打印cwnd变化曲线) | Scapy + matplotlib + pandas | 答案不再是一行数字,而是可视化的动态过程 |
| ④ 错题归因日志 | 记录本次尝试失败原因(如“忘记关闭tcp_syncookies”)、修正步骤、最终验证截图 | Markdown + 截图嵌入 | 形成个人知识漏洞地图,期末复习直击薄弱点 |
5.2 实战案例:用Notebook重做“子网划分”习题(谢希仁P102 T4.3)
假设题目:“某单位分配到一个C类地址192.168.1.0/24,需划分为6个子网,每个子网至少25台主机,求子网掩码和各子网网络地址。”
传统PDF答案只给结果:255.255.255.224,192.168.1.0/27…
而我的Notebook这样实现:
import ipaddress # 步骤1:解析需求 total_subnets_needed = 6 hosts_per_subnet_min = 25 # 步骤2:计算所需主机位和子网位 # 主机位需满足:2^n - 2 >= 25 → n >= 5(因2^5-2=30) host_bits = 5 # C类默认24位网络位,剩余8位,故子网位 = 8 - host_bits = 3 subnet_bits = 8 - host_bits # 验证子网数:2^3 = 8 >= 6 ✓ print(f"主机位:{host_bits}, 子网位:{subnet_bits}, 可用子网数:{2**subnet_bits}") # 步骤3:生成子网列表(自动计算,杜绝手算错误) network = ipaddress.ip_network("192.168.1.0/24") new_prefix = 24 + subnet_bits # /27 subnets = list(network.subnets(new_prefix=new_prefix)) print(f"\n子网掩码: {subnets[0].netmask}") # 输出255.255.255.224 for i, subnet in enumerate(subnets[:6]): # 取前6个 print(f"子网{i+1}: {subnet.network_address}/{new_prefix} " f"(可用主机: {list(subnet.hosts())[0]} ~ {list(subnet.hosts())[-1]})")输出结果:
主机位:5, 子网位:3, 可用子网数:8 子网掩码: 255.255.255.224 子网1: 192.168.1.0/27 (可用主机: 192.168.1.1 ~ 192.168.1.30) 子网2: 192.168.1.32/27 (可用主机: 192.168.1.33 ~ 192.168.1.62) ...为什么这比PDF强?
- 当题目条件变为“需12个子网”,只需改
total_subnets_needed = 12,代码自动重新计算; - 若怀疑某个子网地址算错,直接
print(list(subnets[0].hosts()))看全部主机IP,一目了然; - 所有计算过程可追溯,期末复习时点开Notebook,看到自己当年的推导草稿和debug记录,比翻PDF高效十倍。
5.3 终极习惯:每次打开习题,先写一行%%capture再动手
这是我的后悔药。在Jupyter中,任何代码块前加上:
%%capture # 你的Scapy/Wireshark代码它会捕获所有stdout/stderr输出,避免刷屏干扰。更重要的是,当某次实验失败,%%capture会默默存下所有报错信息——你不必再手忙脚乱复制粘贴,直接翻到单元格下方就能看到完整的traceback。这个习惯让我少浪费30%的debug时间。网络协议的世界没有银弹,只有可复现的步骤、可验证的数据、可追溯的痕迹。希望帮到你。
本文还有配套的精品资源,点击获取