
简介中南大学计算机网络实验源代码是一份面向高校网络专业学生的实践资源聚焦2022年A1、A3两个实验的源码实现适合作为计算机网络课程配套练习。A1覆盖Socket编程与TCP/UDP通信包括连接建立、收发数据、异常处理及IP寻址等传输层基础内容A3进一步深入HTTP等协议报文解析与服务器/客户端交互帮助理解应用层到网络层的分层协作。压缩包共53个文件以C源文件、头文件、目标文件为主体辅以Makefile构建脚本、Python辅助脚本、实验指导docx和运行截图整体仅1.06MB目录结构清晰便于按实验模块查找。已有743人浏览学习。通过阅读并运行这些代码可直观掌握网络编程中的错误捕获、超时重试等关键机制同时结合抓包工具加深对协议栈的认识是一份兼顾理论与实战的参考材料。1. 先从一包实验代码说起A1 与 A3 到底藏着什么这份资源不是“一个能交差的实验报告”拆开后你会发现它实际上承包了中南大学计算机网络实验 A1 和 A3 两个题目的全部交付物。A1 那部分最值得看的是从 network.c 到 mysock_api.c 的一整条自研协议栈配合 client.c 和 server.c 跑双端通信A3 则是一个 Python 写的 HTTP 服务端能把 Hello.html 和 images.jpg 发给浏览器。适用人群很明确正在做或准备做这套实验的学生想补 Socket 和 TCP 原理的 Devops 工程师以及手头有“指导书 源码 运行截图”就能快速复现的人。我的建议是别急着交作业先把这条调用链走一遍哪怕不写一行代码也值。湖科大教书匠、王道那类视频课讲的是通用框架而这份源码是你能亲手跑起来的真实实例每一层都有对应的 .c 文件和 Makefile 规则。对照《计算机网络自顶向下》的传输层章节来读课本上的序号、确认、校验和都能在代码里找到落点理解深度完全不一样。2. 拆开 A1 的骨架从 transport 到 mysock 的四层结构2.1 压缩包里的文件怎么归类打开压缩包第一眼会觉得乱一堆 .c、.h、.o、.swp 混在一起。其实核心文件就三类另两类是编译产物和编辑器残留直接全部扔进 Ubuntu 编译大概率翻车。先把文件分成五组心里就有数了。类别文件说明协议栈源码network.c/h、network_io.c/tcp.c/socket.c、transport.c/h、stcp_api.c/h、mysock.c/mysock_api.c/h、connection_demux.c/h、tcp_sum.c/h自研的简化 TCP/Socket 协议栈应用层源码client.c、server.c、myserver.py、Hello.html双端通信演示程序和 HTTP 服务器脚本与配置hello.sh、ENVCFG.MK、Makefile一键运行、编译环境配置文档与报告附录2-A类实验第1题指导书.docx、附录4-A类实验第3题指导书.docx、Report.txt实验要求与 A1 的文字报告产物与残留各 .o 文件、.swp、.~lock、images.jpg旧机器编译结果、vim 交换文件、A3 截图素材这里面最容易误判的是 .swp 文件。.myserver.py.swp和.Hello.html.swp是 vim 编辑时产生的交换文件不是源码.~lock.附录2…docx#是 LibreOffice 的锁文件也跟代码无关。编译前先make clean或者手动删掉所有 .o不然旧环境的编译结果会干扰你本机的链接过程。images.jpg 是 A3 实验里需要服务器返回的图片资源和 Hello.html 放在同级目录就行。2.2 调用链与每层职责A1 实验不是普通的“调 socket API 收发数据”它的重点是自己在 socket 之上实现一层简化 TCP。整个调用链从应用层往下走大致是这样一个顺序/* 应用层 client.c / server.c ↓ 调用 * Socket API mysock_api.c —— 与 POSIX socket 接口对齐的收发函数 * 简化 TCP stcp_api.c tcp_sum.c —— 序号管理、确认应答、校验和计算 * 连接解复用 connection_demux.c —— 按四元组(源IP/源Port/目的IP/目的Port)分发数据 * IO 抽象 network_io_tcp.c / network_io_socket.c —— 决定数据走真实 socket 还是自制通道 * 底层模拟 network.c —— 模拟链路层/网络层的收包发包 */ /* 最终编译链 * client.o server.o mysock_api.o stcp_api.o * transport.o connection_demux.o tcp_sum.o network_io.o network.o */每层职责一句话能说清mysock_api.c 负责把实验用的接口包装成你熟悉的socket()、bind()、listen()、accept()、send()、recv()的样子stcp_api.c 负责在数据包上打序号和确认号模拟 TCP 的可靠传输tcp_sum.c 计算校验和这是 TCP 头里最容易出错又最不直观的字段connection_demux.c 维护一张哈希表mysock_hash.h按四元组把收到的包分发给对应的连接。看代码的时候重点盯 tcp_sum.c 里的校验和函数。TCP 校验和要覆盖伪头部、TCP 头部和数据三部分伪头部里包括源 IP、目的 IP、协议号、TCP 长度。很多人的包发出去对方没反应不是网络不通而是校验和算错内核直接把包静默丢弃了。这个现象后面避坑章节会细说。2.3 编译环境ENVCFG.MK 与 Makefile 的配合这套代码的 Makefile 写得比较教学化把编译器路径和编译参数拆进了 ENVCFG.MK主 Makefile 通过 include 引入。核心规则大致是这样一个结构# 引入环境配置文件编译器路径、CFLAGS include ENVCFG.MK CC ? gcc CFLAGS ? -O0 -g -Wall -DDEBUG OBJS network.o network_io.o transport.o stcp_api.o \ mysock_api.o connection_demux.o tcp_sum.o all: client server client: client.o $(OBJS) $(CC) $(CFLAGS) -o $ $^ server: server.o $(OBJS) $(CC) $(CFLAGS) -o $ $^ %.o: %.c $(CC) $(CFLAGS) -c -o $ $ clean: rm -f *.o client serverENVCFG.MK 里一般配的是/usr/bin/gcc这类绝对路径和-DDEBUG宏。如果你在自己机器上编译找不到头文件第一反应应该是去改 ENVCFG.MK 而不是动 Makefile。CFLAGS 里-O0是关闭优化-g保留调试信息-DDEBUG会打开代码里#ifdef DEBUG包裹的打印建议保留这三个。这里有个血泪经验OBJS 的顺序不要乱调。链接器按从左到右的顺序解析符号你把 stcp_api.o 放到 transport.o 后面某些老版本 binutils 会报undefined reference to stcp_send。如果真遇到这种报错把报错的函数名对应的 .o 文件往 OBJS 前面挪一行再试。hello.sh 是配套的一键脚本常见做法是先 make再后台启动 server然后运行 client 收发一轮数据最后把两端日志都打出来。用它验证环境是否正常最省事如果 hello.sh 跑通了说明协议栈主链路没问题。3. A3 实验myserver.py 是怎么把 Hello.html 交到浏览器手里的3.1 单线程 HTTP 服务器的实现骨架A3 实验的核心交付物是 myserver.py配合 Hello.html 和 images.jpg 组成一个能跑的 HTTP 服务。压缩包里没有装 Flask、Django 这类框架就是纯标准库 socket 写的。一个典型的单线程 HTTP 服务器骨架长这样#!/usr/bin/env python3 import socket import os HOST 127.0.0.1 PORT 8080 BASE_DIR os.path.dirname(os.path.abspath(__file__)) def render(path: str, conn: socket.socket) - None: if path /: path /Hello.html file_path BASE_DIR path if not os.path.exists(file_path): body b404 Not Found conn.sendall( bHTTP/1.0 404 Not Found\r\n bContent-Type: text/plain\r\n bContent-Length: str(len(body)).encode() b\r\n\r\n body ) return with open(file_path, rb) as f: body f.read() ext path.split(.)[-1] ctype text/html if ext html else image/jpeg resp ( bHTTP/1.0 200 OK\r\n bContent-Type: ctype.encode() b\r\n bContent-Length: str(len(body)).encode() b\r\n\r\n body ) conn.sendall(resp) srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((HOST, PORT)) srv.listen(5) while True: conn, addr srv.accept() data conn.recv(1024) first_line data.split(b\r\n)[0] method, req_path, _ first_line.split() print(method.decode(), req_path.decode()) render(req_path.decode(), conn) conn.close()这个骨架和压缩包里 myserver.py 的职责一致单线程循环、解析请求行、按路径查文件、拼 HTTP 响应头、返回内容。几个参数值得说明listen(5)是待连接队列长度同时来了 5 个连接以内的请求会在内核里排队recv(1024)一次最多读 1KB对 Hello.html 这种小文件够用但如果你把大文件放进去这里必须要改成循环接收否则浏览器会长时间转圈。SO_REUSEADDR这个选项很重要它允许 server 停掉后立即重新绑定同一端口否则你会被 TIME_WAIT 状态卡住几分钟。Content-Type 的判断逻辑按扩展名走html 返回text/htmljpg 返回image/jpeg。这正好对应压缩包里的 Hello.html 和 images.jpg——浏览器拿到图片后能不能正常渲染就看这一行的值写没写对。3.2 用 curl 和浏览器做验收把服务跑起来后最稳的验证方式不是开浏览器而是先用 curl 看原始响应头因为浏览器会把很多错误“美化”掉python3 myserver.py curl -v http://127.0.0.1:8080/Hello.html curl -v http://127.0.0.1:8080/images.jpg正常响应应该长这样状态行是HTTP/1.0 200 OK响应头里有Content-Type: text/html和Content-Length两者缺一个都算不完整。curl 的-v参数会把请求行、响应头全部打印出来这是判断服务器行为最直接的手段。浏览器验证就简单多了地址栏输入http://127.0.0.1:8080/Hello.html能看到一个带标题的页面再访问http://127.0.0.1:8080/images.jpg能直接显示图片。如果 200 返回了但页面白屏先别怀疑 Python 代码回去看 Content-Type 是不是写成了text/plain。3.3 用 Wireshark 验证三次握手与 HTTP 请求实验指导书里的验收往往不只看结果截图还要你解释过程。打开 Wireshark选回环接口lo因为服务器绑定的是 127.0.0.1在过滤器里填tcp.port 8080然后重新执行一次 curl你应该能在抓包列表里看到完整的三次握手第一个包是SYN第二个是SYN-ACK第三个是ACK之后跟着的是 HTTP GET 请求和响应分组。这里大部分人会犯一个低级错误Wireshark 默认打开的是物理网卡接口看不到回环流量一定要手动切到 lo。抓包文件最好存成 .pcap 格式实验报告里贴上三次握手的截图比贴代码更说明问题。指导书里如果要求分析 TCP 报文段结构展开抓包里那个 TCP 分组的Sequence Number和Acknowledgement Number字段就能和 stcp_api.c 里定义的序号逻辑一一对应。4. 避坑记录验收前最容易翻车的五个问题4.1 编译期undefined reference 与被误认的 .swp现象make报错链接阶段出现undefined reference to stcp_send或undefined reference to mysock_accept但源码里明明有这些函数。原因OBJS 列表不完整或者某个 .c 文件没被编译成 .o 放进链接。还有一个隐蔽原因系统里残留了旧版本的 .o 文件链接器优先拿了旧产物符号表对不上。解决先make clean删掉所有 .o再对照 Makefile 手动确认 OBJS 是否包含 tcp_sum.o、connection_demux.o、stcp_api.o。如果还报错把报错符号所在的 .o 文件往 OBJS 列表前面挪。另外copy 压缩包到 Linux 后不要vim直接打开源码又强退留下的 .swp 文件会让人误以为是重复源码ls -a看到.myserver.py.swp就删掉。4.2 运行期端口占用与数据乱序现象server 启动时报Address already in use换端口能跑原端口过几分钟才能再用。原因上一个 server 进程没有正常退出端口处于 TIME_WAIT 状态。TCP 主动关闭的一方要等 2MSL所以立刻重启必然撞车。解决代码里加srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)这是最省事的方案。如果进程还活着用lsof -i :8080查到 PID 再kill掉。别用kill -9先试kill给进程一次释放资源的机会。现象client 连续发多包数据server 端收到的顺序乱了或者丢数据。原因stcp 层只实现了发送没有按序号缓存乱序到达的包或者没有实现超时重传。这个实验里要求自己管序号不是 TCP/IP 协议栈替你兜底。解决检查 stcp_api.c 里发送时是否给每个包赋了递增的 seq接收时是否按 seq 排序再交给上层。对照 tcp_sum.c 的校验逻辑确认收到的包没在校验和这步被丢掉。4.3 验证期白屏与抓不到回环流量现象HTTP 返回 200浏览器却是白屏curl 也看不到内容。原因Content-Length 没写或者写成了和 bytes 长度不一致的值。浏览器等不到完整 body 就放弃渲染表现就是白屏。解决用curl -v看响应头确认Content-Length和 body 实际字节数一致。最稳妥的做法是把 body 读成 bytes 后用len(body)生成长度而不是手写一个数字。现象Wireshark 打开后怎么抓都看不到本机的 HTTP 交换。原因抓包接口选错了抓的是物理网卡而不是回环接口lo。另一个常见原因是过滤器写成了http.server之类不存在的过滤语法导致没有包被显示。解决Wireshark 主界面先选Loopback: lo过滤器用tcp.port 8080。想看 HTTP 层的细节再用http过滤器但前提是前面 tcp 端口过滤能抓到包。5. 进阶把实验代码改成一台看得见的协议示波器5.1 给 myserver.py 加请求日志A3 实验跑通之后我一般会先在 myserver.py 里加一段访问日志把每次请求的方法、路径、状态码和时间都追加到文件里import time LOG_FILE BASE_DIR /access.log def log(method: str, path: str, status: int) - None: line f{time.strftime(%Y-%m-%d %H:%M:%S)} {method} {path} {status}\n with open(LOG_FILE, a) as f: f.write(line)然后在 render 函数的每个 return 前调用log(method, path, 200)或log(method, path, 404)。这样交实验报告时能附带一份真实的访问记录比手画流程图有说服力。注意open(LOG_FILE, a)是追加模式不会覆盖历史日志。5.2 一键自动化验收脚本A1 和 A3 的实验验收有时要跑多轮每次都手动启动、手动 kill 很不值。pytest 级别的测试在这里没必要写个 bash 脚本就够#!/usr/bin/env bash python3 myserver.py SRV_PID$! sleep 1 CODE$(curl -s -o /dev/null -w %{http_code} http://127.0.0.1:8080/Hello.html) kill $SRV_PID if [ $CODE 200 ]; then echo PASS: expected 200, got $CODE else echo FAIL: expected 200, got $CODE exit 1 fi-w %{http_code}只输出 HTTP 状态码-o /dev/null丢弃响应体。这样脚本的输出只有一个数字比较逻辑可以被任何 CI 工具吃掉。以后改完代码跑一遍这个脚本就知道有没有把服务改挂。5.3 先抓包再改代码最后讲一个花过我整晚时间的教训。有一次我改 tcp_sum.c 里的校验和算法怎么改对端都没反应代码读了无数遍发现不了问题。后来拿 Wireshark 抓包才发现问题根本不在校验和而是我定义的结构体里少了#pragma pack编译器按 4 字节对齐把校验和字段挤到了错误位置发出去的包头部就废了。从那以后我每次做网络实验都强制自己先抓一份基线包确认正常路径长什么样再动代码。改完再抓一次包对比两次分组结构问题往往一眼就能看出来。这份资源里的 A1 和 A3 代码足够你把这个流程完整走一遍先跑通再抓包最后改代码。希望帮到你。本文还有配套的精品资源点击获取