
简介本资源是《计算机网络》第五章‘传输层’的课后习题详解答案面向高校计算机、网络工程及相关专业本科生助力理解运输层核心概念与协议机制。内容覆盖5—01至5—20共20道典型习题包括运输层地位与作用辨析、TCP/UDP对比、端口分类、伪首部校验、分片重组、停止等待协议编号机制等关键知识点每题均附逻辑清晰、术语规范的参考解答适合课后巩固、考前复习与自学自查。资源为单个Word文档.doc格式体积精简仅51KB便于快速下载与离线查阅。已有2398人学习下载答案源自教学实践表述严谨、层次分明可直接用于笔记整理、作业核对或课堂讨论参考。1. 这不是“抄答案”而是用第五章习题反向吃透传输层TCP/UDP端口行为、连接状态机与真实网络调试逻辑你手上有份《计算机网络课后习题答案第五章.doc》打开一看全是填空、简答、计算题的标准答案——但如果你真把它当“标准答案”背下来去应付期末考大概率会在实验环节当场卡死Wireshark抓不到三次握手的SYN包、netstat显示LISTEN却curl不通、UDP发包后对方recvfrom()一直阻塞……因为第五章讲的从来不是静态知识点而是传输层协议在操作系统内核、socket API、网络设备三者夹缝中真实运行的动态契约。这份文档的价值不在于告诉你“TCP三次握手要发几个包”而在于帮你把课本里的状态图CLOSED → SYN_SENT → ESTABLISHED…和ss -tuln输出的LISTEN/ESTAB字段、/proc/net/tcp里那一串十六进制数、甚至tcpdump -i any port 8080抓到的RST包对应起来。它适合两类人一是正在啃《自顶向下》或《Kurose》第五章、被“拥塞控制窗口”绕晕的本科生二是刚接手微服务端口治理、发现java.net.BindException: Address already in use却查不出哪个进程占了8080的DevOps工程师。本文不复现.doc文件内容而是以该文档覆盖的全部典型习题为路标带你亲手跑通5个可验证的传输层关键场景——从最简socket通信到端口冲突排查每一步命令都带内核级解释。2. 用最小代码复现第五章核心习题从UDP无连接到TCP可靠传输的完整链路第五章习题高频聚焦于传输层两大协议的行为差异与实现约束。与其死记“UDP是无连接的”不如直接用两行Python让这个概念具象化一个UDP客户端发包后不等响应就退出而TCP客户端必须收到服务端ACK才能进入ESTABLISHED状态。本章将用可执行代码还原习题中所有关键场景所有脚本均在LinuxUbuntu 22.04和macOSVentura实测通过无需安装额外依赖仅需Python 3.8。2.1 UDP端口绑定与数据报发送验证“无连接”本质与端口复用限制第五章习题常问“为什么UDP服务器可以bind(0.0.0.0:8080)而多个UDP客户端也能同时bind(0.0.0.0:8080)”这背后是SO_REUSEADDR套接字选项的底层机制。我们用以下脚本验证# udp_server.py import socket import sys server_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 关键启用端口复用允许多个socket绑定同一端口 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((0.0.0.0, 8080)) print(UDP server listening on :8080) while True: data, addr server_socket.recvfrom(1024) print(fReceived from {addr}: {data.decode()}) server_socket.sendto(bACK, addr)# udp_client.py import socket import sys client_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 不bind() —— 客户端通常让内核自动分配临时端口ephemeral port # 若显式bind((0.0.0.0, 8080))则需同样设置SO_REUSEADDR才不冲突 client_socket.sendto(bHello UDP, (127.0.0.1, 8080)) data, _ client_socket.recvfrom(1024) print(fServer reply: {data.decode()})逻辑说明UDP服务器bind()时指定SO_REUSEADDR意味着允许其他socket包括其他UDP服务也绑定同一端口只要它们也设置了该选项。而客户端默认不bind内核从ephemeral port rangeLinux默认32768–60999中随机选一个端口作为源端口。若强行让两个客户端都bind((0.0.0.0, 8080))且未设SO_REUSEADDR第二个会报OSError: [Errno 98] Address already in use——这正是习题中“端口已被占用”的真实来源而非IP地址冲突。参数说明socket.SOCK_DGRAM明确声明UDP协议recvfrom()返回(data, address)元组体现UDP面向报文的特性sendto()需显式指定目标地址因UDP无连接上下文。2.2 TCP三次握手全过程观测用netstat tcpdump交叉验证状态机第五章必考题“画出TCP三次握手状态变迁图并指出客户端与服务端各自处于什么状态”。光画图没用必须看到真实状态。启动一个极简TCP服务再用系统工具追踪# tcp_server.py import socket server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((0.0.0.0, 8081)) server_socket.listen(1) # backlog1简化状态观察 print(TCP server listening on :8081) conn, addr server_socket.accept() print(fConnection established with {addr}) conn.close() server_socket.close()启动服务后立即执行三组命令# 终端1实时观察socket状态-t TCP, -n 数字端口, -l 仅监听, -p 显示PID watch -n 0.5 ss -tlnp | grep :8081 # 终端2抓取三次握手全过程-i any 抓所有接口-c 10 限制10包 sudo tcpdump -i any -c 10 port 8081 and (tcp-syn or tcp-ack or tcp-rst) # 终端3发起连接触发三次握手 curl -v http://127.0.0.1:8081预期现象ss输出中服务端始终显示LISTEN对应LISTEN状态tcpdump应捕获3个包SYN客户端→服务端、SYN-ACK服务端→客户端、ACK客户端→服务端curl结束后ss可能短暂出现ESTAB若服务端accept()成功但因服务端立即close()很快变为FIN-WAIT-1或消失。关键解读ss -tlnp中的状态列State直接对应TCP状态机。LISTEN即服务端调用listen()后的状态ESTAB是三次握手完成后双方共同进入的状态FIN-WAIT-1出现在主动关闭方调用close()后。课本状态图里的每个节点在这里都是可观察、可测量的真实内核状态。2.3 端口复用与冲突实战为什么0.0.0.0:80被占 ≠ 127.0.0.1:80被占第五章习题常设陷阱“0.0.0.0:80被占用是否意味着所有IP的80端口都不能用了”答案是否定的——0.0.0.0是通配符地址表示监听本机所有网卡但具体冲突取决于socket的bind()行为与内核端口查找逻辑。我们用以下命令验证# 启动一个占住0.0.0.0:80的服务如简易HTTP服务 python3 -m http.server 80 --bind 0.0.0.0 # 查看当前占用情况 sudo ss -tuln | grep :80 # 尝试绑定127.0.0.1:80 —— 会失败因为0.0.0.0:80已覆盖该地址 sudo python3 -c import socket; s socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); s.bind((127.0.0.1, 80)); s.listen(1) # 尝试绑定[::1]:80IPv6本地环回—— 成功因IPv4与IPv6端口空间独立 sudo python3 -c import socket; s socket.socket(socket.AF_INET6); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); s.bind((::1, 80)); s.listen(1) 原理深挖Linux内核在bind()时执行端口冲突检查规则是若已有socket绑定0.0.0.0:PORT则任何绑定*.*:PORT包括127.0.0.1:PORT、192.168.1.100:PORT都会失败但[::1]:PORT属于IPv6地址族与IPv4端口空间物理隔离故可共存。这是习题中“端口地址绑定范围”概念的工程落地也是生产环境Nginx/Apache配置listen 127.0.0.1:80与listen [::1]:80并存的底层依据。3. 从习题错误答案反推TCP连接重用、TIME_WAIT与端口耗尽的三大避坑点第五章习题答案文档里常出现看似正确实则危险的结论比如“客户端主动关闭后端口立即可用”或“TIME_WAIT状态只持续30秒”。这些表述在理想实验室环境成立但在高并发生产系统中会引发严重问题。本章基于真实翻车案例列出3个高频踩坑点每条均附现象、根因与可验证的解决命令。3.1 现象频繁创建短连接的客户端报错“Address already in use”ss -tan | grep TIME-WAIT显示上千条记录原因TCP连接主动关闭方通常是客户端进入TIME_WAIT状态持续2×MSLLinux默认60秒期间该四元组源IP:源端口:目的IP:目的端口不可复用。若客户端每秒新建100个连接60秒内将累积6000个TIME_WAIT socket迅速耗尽ephemeral port range默认约28K导致bind()失败。解决短期调整内核参数加速回收仅限客户端可控场景# 允许TIME_WAIT socket被快速重用需服务端配合见下条 echo 1 | sudo tee /proc/sys/net/ipv4/tcp_tw_reuse # 缩短TIME_WAIT超时不推荐违反RFC echo 30 | sudo tee /proc/sys/net/ipv4/tcp_fin_timeout长期改用连接池如requests.Session或长连接避免高频短连。3.2 现象服务端重启后无法bind(:8080)报错“Address already in use”但netstat -tuln | grep 8080无输出原因服务端主动关闭连接时也进入TIME_WAIT若它先发FIN且bind()时未设SO_REUSEADDR。内核拒绝新socket绑定因旧TIME_WAIT socket仍占据该端口。解决必须在服务端socket上设置SO_REUSEADDR如2.1节所示验证命令ss -tan state time-wait sport :8080查看是否有残留TIME_WAIT。3.3 现象UDP服务在高负载下丢包netstat -s -u显示“packet receive errors”激增但ping网络通畅原因UDP socket接收缓冲区rmem_default过小内核来不及将网卡DMA收到的数据拷贝到应用buffer导致后续包被丢弃。这不是网络问题而是本地资源瓶颈。解决动态调大UDP接收缓冲区# 查看当前值 cat /proc/sys/net/core/rmem_default # 临时增大单位字节 echo 4194304 | sudo tee /proc/sys/net/core/rmem_default # 永久生效写入/etc/sysctl.conf echo net.core.rmem_default 4194304 | sudo tee -a /etc/sysctl.conf sudo sysctl -p应用层需用setsockopt(SO_RCVBUF)进一步扩大且需在bind()前调用。血泪经验TIME_WAIT问题在微服务间调用如Spring Cloud Gateway频繁请求下游中极为常见。曾有个项目因未设tcp_tw_reuseQPS超过200后API成功率骤降至70%。而UDP缓冲区问题在IoT网关接收海量设备心跳包时高频出现——netstat -s -u的receive errors指标比ping更能反映真实瓶颈。4. 用第五章习题反向构建传输层故障排查树从curl失败到定位内核参数第五章习题本质是传输层故障的微型沙盒。当你遇到curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused别急着重启服务按以下排查树逐层收缩问题域。该树完全基于第五章覆盖的协议机制设计每步命令均可在终端直接执行。4.1 第一层确认服务进程是否存在且监听正确地址/端口# 查看所有监听TCP端口及对应PID sudo ss -tuln | grep :8080 # 若无输出 → 服务未启动或bind失败 # 若输出为 127.0.0.1:8080 → 只监听本地环回外部IP不可达 # 若输出为 0.0.0.0:8080 → 监听所有地址继续下一步4.2 第二层验证端口是否被防火墙拦截Linux iptables / ufw# Ubuntu ufw状态 sudo ufw status verbose # 检查iptables INPUT链重点看DROP规则 sudo iptables -L INPUT -vn # 临时放行8080端口测试 sudo ufw allow 8080 # 或临时清空iptables仅测试用 sudo iptables -P INPUT ACCEPT sudo iptables -F4.3 第三层检查TCP连接建立过程是否卡在SYN阶段# 在服务端执行捕获SYN包客户端发SYN服务端未回复SYN-ACK sudo tcpdump -i any -c 5 tcp[tcpflags] tcp-syn ! 0 and port 8080 # 若捕获到SYN但无SYN-ACK → 服务端进程崩溃、内核丢包或路由问题 # 若完全捕获不到SYN → 客户端网络问题或防火墙拦截SYN4.4 第四层确认客户端ephemeral端口是否耗尽# 查看客户端可用端口范围 cat /proc/sys/net/ipv4/ip_local_port_range # 统计当前已用临时端口数 ss -tan | awk {S[$1]} END {for(a in S) print a, S[a]} | grep TIME-WAIT\|ESTAB # 若TIME-WAIT数量接近端口范围上限如60000-3276827232即告警4.5 第五层终极验证——用raw socket绕过socket API直连当以上步骤均正常但应用层仍失败可能是glibc或JVM socket封装层bug。此时用Python raw socket直发SYN包验证内核协议栈# syn_flood_test.py 仅用于验证非攻击 from scapy.all import * ip IP(dst127.0.0.1) tcp TCP(dport8080, flagsS, seq1000) pkt ip/tcp # 发送SYN并等待响应 response sr1(pkt, timeout2, verbose0) if response and response.haslayer(TCP): if response[TCP].flags 0x12: # SYN-ACK print(Kernel TCP stack OK: SYN-ACK received) elif response[TCP].flags 0x14: # RST print(Service not listening or firewall blocking) else: print(No response — network or routing issue)提示此脚本需pip install scapy且sudo权限。若能收到SYN-ACK证明内核协议栈完好问题一定在应用层如服务代码未listen()、或bind()后未listen()。这是第五章“协议栈分层”思想的终极实践——把问题精准锚定在TCP层还是应用层。5. 把习题答案变成生产力用Python自动化解析TCP状态、生成端口健康报告第五章习题答案文档里那些静态表格如TCP状态迁移条件、UDP校验和计算步骤完全可以转化为运维脚本。我日常用一个200行Python脚本每天凌晨自动扫描服务器所有监听端口生成HTML报告包含三项核心指标端口存活性、TIME_WAIT占比、接收错误率。这比人工查netstat高效十倍且能提前预警端口耗尽风险。5.1 核心数据源直接读取/proc/net/接口获取内核级状态Linux内核通过/proc/net/伪文件系统暴露TCP/UDP统计信息比ss/netstat更底层、更实时文件作用关键字段/proc/net/tcpTCP连接全量快照slsocket序号、local_address十六进制IP:端口、st十六进制状态码、tx_queue/rx_queue发送/接收队列长度/proc/net/snmp协议统计汇总Tcp:行后第10列AttemptFails连接失败数第12列EstabResets异常断连数/proc/net/snmp6IPv6统计同上但针对IPv6状态码解密/proc/net/tcp中st字段是十六进制01ESTABLISHED,0ALISTEN,06TIME_WAIT。可用Python快速转换int(0A, 16)→10→LISTEN对照/usr/include/asm-generic/errno.h5.2 自动化报告生成脚本精简版#!/usr/bin/env python3 # port_health_report.py import re import subprocess from datetime import datetime def parse_tcp_states(): 解析/proc/net/tcp统计各状态连接数 states {ESTABLISHED: 0, LISTEN: 0, TIME_WAIT: 0, CLOSE_WAIT: 0} with open(/proc/net/tcp, r) as f: next(f) # skip header for line in f: parts line.split() if len(parts) 4: continue st_hex parts[3] st_dec int(st_hex, 16) if st_dec 1: states[ESTABLISHED] 1 elif st_dec 10: states[LISTEN] 1 elif st_dec 6: states[TIME_WAIT] 1 elif st_dec 8: states[CLOSE_WAIT] 1 return states def get_udp_errors(): 从/proc/net/snmp提取UDP接收错误 with open(/proc/net/snmp, r) as f: for line in f: if line.startswith(Udp:): # Udp: inErrors 字段是第6个索引5 return int(line.split()[5]) return 0 def generate_html_report(): states parse_tcp_states() udp_errors get_udp_errors() total_tcp sum(states.values()) # 计算健康度 tw_ratio states[TIME_WAIT] / total_tcp if total_tcp else 0 health_score 100 - min(50, tw_ratio * 100) # TIME_WAIT超50%扣50分 html fhtmlbody h2端口健康报告 - {datetime.now().strftime(%Y-%m-%d %H:%M)}/h2 pstrongTCP状态分布/strong ESTAB:{states[ESTABLISHED]} | LISTEN:{states[LISTEN]} | TIME_WAIT:{states[TIME_WAIT]} ({tw_ratio:.1%}) | CLOSE_WAIT:{states[CLOSE_WAIT]}/p pstrongUDP接收错误/strong {udp_errors} 次阈值100告警/p pstrong健康评分/strong {health_score:.0f}/100/p /body/html with open(/var/log/port_health.html, w) as f: f.write(html) if __name__ __main__: generate_html_report()5.3 部署为定时任务并集成告警# 添加到crontab每5分钟执行一次 echo */5 * * * * /usr/local/bin/port_health_report.py | sudo crontab - # 配合curl发送企业微信告警当TIME_WAIT占比30%时 # 在generate_html_report()末尾添加 if tw_ratio 0.3: subprocess.run([ curl, -X, POST, https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY, -H, Content-Type: application/json, -d, {msgtype: text, text: {content: ⚠️ TIME_WAIT占比过高: %.1f%%}} % (tw_ratio*100) ])玄学技巧/proc/net/tcp的tx_queue和rx_queue字段第4、5列是判断连接是否卡死的关键。若ESTABLISHED连接的tx_queue 10000说明应用层write()后内核发送队列积压大概率是下游服务响应慢或网络拥塞。这比单纯看连接数更能发现隐性故障。我坚持用这套脚本三年帮团队提前发现过7次端口耗尽事故其中3次发生在凌晨2点避免了白天业务高峰故障。它把第五章那些“TCP状态机”、“UDP校验和”、“端口复用规则”全部变成了可量化、可告警、可追溯的生产资产。而不是锁在.doc文件里等着期末考完就删除。希望帮到你。本文还有配套的精品资源点击获取