简介面向网络测量课程的一套拓展实验方案基于Mininet仿真平台与Ryu控制器完成SDN网络测量实验随包提供Python源码、文档说明与使用说明适合网络工程、通信工程、自动化、电子信息等计算机相关专业的在校学生、教师或企业开发人员用于课程设计、毕业设计、项目初期演示等场景也可作为初学者理解SDN数据平面测量机制的入门案例。压缩包共23个文件体积约549KB核心为12个Python脚本分别承担拓扑构建、流量生成、EverFlow策略测量、Ryu控制器测试等功能另有6张拓扑或架构示意图、2个HTML辅助说明页面、README与License文件便于按需查阅并快速复现实验。已有164人学习下载代码经过运行验证答辩评审平均分达到96分说明工程完成度较高适合直接用于课题演示或功能扩展。资源不是简单给出运行代码而是通过拓扑结构图、运行日志及说明文档完整呈现从环境搭建、测量执行到结果验证的流程并给出EverFlow、Ryu测量等典型实现便于读者模仿和改造。借助这批文件可以快速搭建基于Mininet和Ryu的测量实验环境深入理解SDN网络中流量采集与测量机制也可作为毕业设计或课程设计的可靠基础整体实用性强。1. 这门拓展实验在解决什么用软件定义网络把“黑匣子”拆开看网络课程的期末拓展实验如果只给一个题目通常意味着后面还有答辩和代码审查。这次要做的“基于 Mininet 和 Ryu 的 SDN 网络测量实验”核心不是重造一个路由器而是两件事用 Mininet 在笔记本上模拟出一张多交换机网络拓扑用 Ryu 控制器站在 OpenFlow 协议层把这张网络上经过的流、端口计数和链路状态“测量”出来。换句话说你在做实验时根本不需要真实的交换机也不需要几个人一起去机房排线一台机器就能跑完整个实验闭环而且输出的是等价的真实转发与统计报文。这个项目适合几类人网络课程需要交拓展实验的本科生或研究生、准备 SDN 方向入门但不想直接上手控制器底层开发的工程师、以及想验证“用软件定义的思想做流量观测”这个方向是否可行的学习者。它和传统测量的区别在于传统网络里网管只能看 SNMP 计数器想知道某一条流走哪条路径、每跳丢多少包往往要额外部署探针而 SDN 里控制器本身就掌握全网的流表项流量统计是协议自带的能力你要做的是把统计报文解析出来并呈现成可读的数据。这就是我把它称为“把黑匣子拆开看”的原因。2. 为什么 Mininet 加 Ryu 能撑起网络测量原理、分工与指标选型2.1 Mininet 与 Ryu 的分工虚拟拓扑与控制器各管什么先理清角色。Mininet 是一个网络仿真平台它利用 Linux 内核的网络命名空间和 Open vSwitch 的软件交换机在普通的 x86 机器上创建出隔离的主机节点、交换机节点和链路。在代码里你定义一个topo对象比如linearTopo(3)Mininet 就会生成 3 台互相串联的交换机每台交换机下接一台主机。这些节点在系统里各自有独立的网络栈进程间通过虚拟网卡和 veth pair 转发数据跟在真实硬件上跑的效果几乎相同。Ryu 则是运行在用户态的 OpenFlow 控制器。它做的事情是与交换机建立 TCP 连接监听 OpenFlow 消息比如OFPT_PACKET_IN、OFPT_FLOW_STATS_REPLY和OFPT_PORT_STATS_REPLY然后根据你的策略下发flow_mod规则或者返回测量数据。在这个项目里Mininet 负责提供“物理”设备和流量发生器Ryu 负责搜集“测量读数”二者通过 OpenFlow 通道对话。分工的关键在于测量脚本不应该直接读虚拟网卡的计数器而应从控制器侧取数因为只有控制器侧才能按流flow维度区分流量。比如带宽、丢包这类指标如果按端口统计一台交换机所有流量混在一起你根本看不出是哪条业务流而按流统计则可以精确到 src_ip、dst_ip、tcp_port 的任意组合。2.2 OpenFlow 让测量下沉到流表比 SNMP 细在哪儿传统网络里做测量SNMP 是最常见的方案但它的粒度是“接口”而且轮询周期长一般 30 秒起步。OpenFlow 协议从 1.0 开始就定义了两种统计消息OFPFlowStatsRequest用来查询流表项OFPPortStatsRequest用来查询物理端口。控制器下发的请求到达交换机后交换机会把匹配到的每条流表项的packet_count、byte_count、duration_sec打包成OFPFlowStatsReply返回。相比 SNMP这种测量的细粒度优势是结构化的你可以用OFPFlowMod的匹配字段去圈定某一条流比如匹配in_port1和ipv4_src192.168.1.0/24交换机就只统计这个范围的流量也可以把流量镜像到某端口用控制器去读统计。决定一个测量实验效果好坏的关键往往就在于你发的统计请求间隔多大、匹配字段怎么设。这个项目里我们通常用两个统计周期就够了一个短周期1 到 2 秒用于观测突发流量一个长周期10 秒左右用于计算平均带宽。注意不要简单把两个指标混在一起否则数据会显得非常毛糙——这属于实验设计层面的细节比赛和答辩很看重这一点。2.3 三个核心测量指标怎么选带宽、RTT 与丢包率的计算口径题目里的“网络测量”通常被展开成三个请求频率最高的指标带宽与吞吐率。这个指标有两种测法用iperf3主动灌入 TCP/UDP 流量然后取交换机流表里的byte_count差值除以统计间隔得到的就是该流在交换机上的瞬时吞吐率另一种是控制器周期性发OFPPortStatsRequest按端口差值得出链路利用率。前者按流测量适合做业务监测后者按端口测量适合定位链路瓶颈。RTT 与时延。用ping测的是主机应用层的往返时延包含排队时延和系统调用开销用 OpenFlow 的OFPExperimenterStatsRequest或通过流量识别来推算的是转发面时延数值往往低一个数量级。课堂教学里通常两者都测然后对比说明软件转发与内核协议栈的差异。丢包率。丢包的严格定义是发送方发送的字节数与接收方收到的字节数之差占发送比例。在 SDN 拓扑里如果 UDP 流量没有配流表规则交换机默认丢包这时控制器能同时查到上游端口统计发出与下游端口统计收到两者相减就能算出精确的丢包量这在真实网络里是做不到的是 SDN 测量最有说服力的卖点。在写代码之前建议先把这三个口径写在实验报告的开头因为评分的标准往往就是“指标定义是否清楚、测量是否基于协议原语”而不是你刷新了多少行日志。3. 把测量环境从零到一搭起来版本、安装与控制器代码解读3.1 环境版本与 Python 依赖为什么我推荐 Ubuntu 20.04 Python 3.8我在踩完一遍版本坑之后最稳妥的组合是 Ubuntu 20.04Python 3.8 到 3.10 之间Ryu 4.34Mininet 2.3.0。这个组合里 Mininet 的脚本和 Open vSwitchOVS内核模块配合最顺Ryu 的依赖库也都有编译好的 wheel不需要从源码现场编译。如果你是用虚拟机跑记得把虚拟网卡模式设为桥接或 NAT保证 Mininet 的虚拟网卡之间能有正确的三层路由如果你是在 Windows 上用 WSL2建议直接用apt install mininet而不是在 WSL 里手动编译否则常遇到netlink通信失败。Ryu 需要 Python 环境注意它依赖eventlet、msgpack、oslo.config这几个库安装时如果出现 gcc 编译报错多半是 Python 版本太高3.11 及以上导致某个 C 扩展没有对应的 wheel这时不要硬编换成 3.10 装会更快。下面是在 Fresh Ubuntu 20.04 环境里的标准操作# 1. 系统更新并安装基础网络工具 sudo apt update sudo apt upgrade -y sudo apt install -y git net-tools tcpdump iperf3 python3-pip python3-venv # 2. 安装 MininetUbuntu 20.04 自带 2.3.0官方源较稳定 sudo apt install -y mininet # 3. 创建虚拟环境并安装 Ryu避免和系统 Python 冲突 python3 -m venv ~/sdn_venv source ~/sdn_venv/bin/activate pip install --upgrade pip pip install ryu4.34解释下参数python3-venv用来隔离 Ryu 的依赖避免污染系统 Pythontcpdump和iperf3分别用于抓包和测试流量生成两者都是测量实验最常用的配套工具。安装完成后执行mn --version应显示2.3.0ryu-manager --version应显示4.34。如果mn提示找不到ovs-vsctl还需要补一条sudo apt install -y openvswitch-switch这是 Mininet 后端依赖的虚拟交换机。3.2 网络测量控制器的骨架Ryu 请求流表统计的两种方式Ryu 写测量程序并不复杂核心就是三个回调交换机接入上报EventOFPSwitchFeatures、周期发送统计请求send_msg、收到统计回复后解析EventOFPFlowStatsReply和EventOFPPortStatsReply。常见做法是开一个绿色线程每 5 到 10 秒轮询一次所有已接入交换机的流表项。下面是一段最小可用的控制器代码名字就叫measure.pyfrom ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib import hub class NetworkMeasure(app_manager.RyuApp): OFP_VERSIONS [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.datapaths {} # 启动一个后台协程每 10 秒请求一次统计 self.monitor_thread hub.spawn(self._monitor) set_ev_cls(ofp_event.EventOFPSwitchFeatures, MAIN_DISPATCHER) def switch_features_handler(self, ev): dp ev.msg.datapath self.datapaths[dp.id] dp self.logger.info(Switch %s connected, dp.id) def _monitor(self): 周期性地向所有交换机发送统计请求 while True: for dp in self.datapaths.values(): # 构造并发送流表统计请求 req dp.ofproto_parser.OFPFlowStatsRequest(dp) dp.send_msg(req) # 构造并发送端口统计请求 req_port dp.ofproto_parser.OFPPortStatsRequest( dp, 0, dp.ofproto.OFPP_ANY) dp.send_msg(req_port) hub.sleep(10) set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def flow_stats_reply(self, ev): 解析流表统计按流表项输出累计包数和字节数 for stat in ev.msg.body: self.logger.info( flow: table%s packets%s bytes%s duration%ss, stat.table_id, stat.packet_count, stat.byte_count, stat.duration_sec )这里的逻辑可以拆成三块理解。第一hub.spawn(self._monitor)是 Ryu 的协程机制它不会阻塞主循环且可以同时管理多台交换机这正是测量系统需要的并发能力。第二OFPFlowStatsRequest和OFPPortStatsRequest是协议层面的两类不同原语流表统计按“匹配规则”聚合端口统计按“物理端口”聚合两者可以互相印证。第三回调函数里的ev.msg.body是交换机回复的统计列表每条流表项都携带包数、字节数和存活时长这些原始数据就是后续计算带宽和丢包率的基础。需要提醒的是_monitor里的hub.sleep(10)是测量精度与控制器开销的折中间隔太短交换机会频繁上送统计CPU 占用高测量结果也容易出现毛刺间隔太长则抓不住瞬时拥塞。我在做实验时一般先按 10 秒跑通流程再改成 2 秒细看某条流的行为。另外Ryu 默认监听 6653 端口而 Mininet 的默认控制器端口在 2.3.0 之后也是 6653两者能直接对上如果你在旧版 Mininet 里遇到连不上把 Ryu 改成监听 6633 即可这属于最常见的版本错配。3.3 测量客户端的设计用 iperf3 制造流量并用 Python 持久化数据只搭控制器不成“测量”必须有流量经过流表才有数据可读。最可靠的方式是用 iperf3 在主机之间产生 TCP/UDP 流量同时让控制器侧周期性地抓取统计。下面这段 Python 脚本可以放在 Mininet 的命令行里执行但更专业的方式是把它当成独立的测量控制脚本在宿主机上通过mnexec或mininet.rpc在特定主机里启动 iperf 进程。import subprocess import time import csv import json def run_iperf3(host_ip, duration10, udpFalse, bandwidth10M): 在目标主机上执行 iperf3 测量返回 JSON 结果 cmd [ iperf3, -c, host_ip, -t, str(duration), -J # 以 JSON 格式输出方便程序解析 ] if udp: cmd [-u, -b, bandwidth] # UDP 模式需要指定带宽 result subprocess.run(cmd, capture_outputTrue, textTrue, timeoutduration 5) # iperf3 服务端需要提前在目标主机启动否则会连接失败 return json.loads(result.stdout) def append_to_csv(row, filepathmeasure_result.csv): with open(filepath, a, newline) as f: writer csv.writer(f) writer.writerow(row)代码里的-J参数让 iperf3 输出结构化 JSON客户端进程结束后解析即可获得速率、重传和抖动数据。-u -b 10M是 UDP 模式下的参数表示目标速率 10 Mbps适合用来测试丢包率TCP 模式则不需要-b因为拥塞控制会自动调整。实际运行时我通常在拓扑内的 h1 启动服务端在 h2 上执行客户端然后在控制器日志里对照流表统计就能同时拿到两个视角的测量结果应用的实时吞吐率报文和交换机侧的累计字节数差值。为什么要多一个 Python 封装层而不是直接在终端敲iperf3因为实验往往要跑多轮改变链路带宽、增加背景流量、拔掉某条链路。用 Python 把每一轮的结果写入独立的 CSV并按时间戳归档后续画图和分析时就非常省事答辩时也容易呈现数据之间的因果关系。这属于项目里“额外加分的可持续性设计”。4. 跑通完整测量链路从拓扑启动到数据落盘的标准流程4.1 启动顺序有讲究先开控制器还是先开 Mininet很多人第一次做这个实验会下意识先启动 Mininet再运行ryu-manager结果是交换机在找不到控制器时反复尝试连接日志里一片红。正确的顺序是先启动控制器让它守候在端口上再启动 Mininet这时交换机一启动就能完成 TCP 握手并立刻上报features。先在一个终端里启动 Ryusource ~/sdn_venv/bin/activate cd ~/sdn_measure ryu-manager measure.py # 默认监听 6653 端口看到Switch 1 connected或Switch 2 connected之后再打开另一个终端启动拓扑sudo mn --topolinear,3 \ --controllerremote,ip127.0.0.1,port6653 \ --switchovsk,protocolsOpenFlow13 \ --mac几个参数需要解释。--controllerremote,ip127.0.0.1,port6653是指定把 Mininet 里的交换机向本机的 6653 端口发起连接如果 Ryu 监听的是 6633 就在这里改成 6633--switchovsk,protocolsOpenFlow13明确使用 OpenFlow 1.3 协议版本这与代码里OFP_VERSIONS [ofproto_v1_3.OFP_VERSION]必须一致否则握手时会因为协议版本协商失败而断开--mac让每台主机用类似00:00:00:00:00:01的固定 MAC方便后续在流表匹配里直接辨认主机。启动成功后Mininet 会进入交互式 CLI此时可以用net查看拓扑、用dump查看各节点负载。4.2 制造流量并采集从 ping 到 iperf3 的三种测量场景拓扑稳定之后就可以按场景造流量了。最简单的测量是ping在 Mininet CLI 里执行h1 ping h2此时控制器如果实现了转发逻辑就会在流表里新增一条匹配ipv4_src10.0.0.1,ipv4_dst10.0.0.2的流表项对应packet_count会逐渐增加。如果你还开发了自动下发流表的功能这一步的统计值会稳定增长如果没有流量会交给控制器做软件转发统计值也会涨但性能差很多。第二种是持续带宽测量用 iperf3 打满一条链路# 在 h1 上启动 iperf3 服务端 h1 iperf3 -s -p 5201 # 在 h2 上启动客户端持续 20 秒每 2 秒打印一次速率 h2 iperf3 -c 10.0.0.1 -p 5201 -t 20 -i 2这里-i 2表示每 2 秒输出一次即时吞吐率控制器侧每 10 秒的流表统计可以和它对照验证。第三次是丢包测量# 在 h2 上向 h1 持续发送 5000 个 UDP 包每个包 100 字节限制速率 100M h2 iperf3 -c 10.0.0.1 -u -b 100M -t 10 -l 100UDP 模式不会重传所以h1收到的包数量一定小于等于发送量差值就是丢包数。把这组数字和控制器端口统计里rx_dropped对比基本能定位丢包发生在哪个交换机。4.3 读数对照为什么要同时记录应用层结果与交换机侧计数实验报告里最有说服力的一张表是对比“应用层测量结果”和“OpenFlow 统计结果”两张数据。例如 iperf3 说平均带宽是 92.5 Mbps而控制器在两段时间间隔里从流表算出的吞吐率是 93.1 Mbps两者误差在 1% 以内这就交叉验证了测量链路的正确性。下面是典型的输出对照结构你完全可以照搬到报告里测量对象来源间隔典型值备注TCP 吞吐率iperf3 JSON 输出1s92~94 Mbps受拥塞窗口影响流表字节数增量Ryu OFPFlowStatsReply10s93.1 Mbps取两次统计差值RTTMininet ping1s0.12~0.25 ms比物理设备低很多端口丢包计数Ryu OFPPortStatsReply10s0注意 rx_dropped 字段比较这两组数是排除“假统计”最直接的方法。如果两侧差别超过 5%优先检查是不是控制器轮询间隔内流表项被 idle_timeout 清掉了或者统计请求被交换机端排队。5. 搭建与测量中的常见问题排查十个里有八个是这几个坑5.1 Ubuntu 16.04 上安装 Ryu 反复报编译错误现象pip install ryu4.34时在安装eventlet或oslo.config时 gcc 报错错误信息指向某些 C 头文件缺失反复尝试换源也无济于事。原因Ubuntu 16.04 自带的 Python 是 3.5而新版 Ryu 的依赖eventlet0.30已经放弃了对 Python 3.5 的预编译 wheelpip 只能现场编译编译过程又缺少python3-dev头文件自然失败。解决如果你必须留在 16.04可以安装旧版 Ryupip install ryu4.30或者先apt install python3-dev libffi-dev libssl-dev再重试如果条件允许直接升级到 Ubuntu 20.04 加 Python 3.8 最省事这也是我后来长期使用的环境。5.2 交换机连上了控制器但流表统计始终为零现象ryu-manager启动后能看到 Switch connected 日志但OFPFlowStatsReply回调里body为空列表或者只有表头的两个条目。原因控制器只发统计请求并不代表有流量经过如果你没有往任意主机 ping 或跑 iperf3交换机的流表本身就没有业务条目统计自然为空。另外有一个隐蔽点如果控制器逻辑是“收到 packet_in 后再下发流表”而流量只是 ping过几秒流表项可能因idle_timeout从交换机里被移除统计就归零了。解决先在 Mininet CLI 里执行h1 ping h2 -c 20制造稳定流量然后立刻观察统计如果使用 iperf3让-t的时间大于控制器轮询周期保证至少跨越两个统计点。5.3 用 ping 测出时延与控制器侧计算的时延差了一个量级现象h1 ping h2的 RTT 稳定在 0.2 ms 左右但从packet_in时间戳和packet_out时间戳算出的转发时延只有 0.02 ms。原因这其实是正常的。ping 测的是主机网络协议栈的完整往返包含系统调用、ARP 处理、套接字缓冲和中断调度而 OpenFlow 测量的只是交换机从收到包到转发出去的时间差去掉了主机的所有开销。这个差异恰好说明了控制平面和数据平面的分离。解决报告中明确标注“ping 时延为端到端应用层时延控制器时延为转发面时延”两者不构成矛盾如果在更复杂的多交换机拓扑里可以用ping的差值除以跳数得到每跳平均时延再与单跳转发时延对照。5.4 脚本执行完了CSV 文件却没有任何数据现象在终端里运行 Python 测量脚本控制台打印有数据但打开measure_result.csv只有表头。原因绝大多数情况下是csv.writer写入的内容没有 flush 到磁盘尤其在进程被 CtrlC 强制终止时缓冲区随进程消失。另一个常见原因是脚本里没有使用with open()而是手动f open()然后忘记f.close()。解决统一使用上下文管理器或者明确调用f.flush()。如果要在整个测量过程中持续写入建议每条数据写入后立即flush()保证中断时数据不丢。这个坑在真实测量里非常普遍属于“看起来是小问题到了答辩现场就翻车”的点。5.5 Mininet 启动时报错 openvswitch 或 cgroup 相关异常现象sudo mn --topolinear,3 --controllerremote启动时报Cannot find required executable ovs-vsctl或者提示cgroup目录不存在。原因前者是缺少 Open vSwitch 用户态工具openvswitch-switch包后者常见于容器或精简系统环境下没有正确挂载内核 cgroup 文件系统。解决先执行sudo apt install -y openvswitch-switch再执行sudo service openvswitch-switch start如果 cgroup 报错重建 sysfssudo mount -t cgroup2 cgroup2 /sys/fs/cgroup或重启后重新启动 Mininet。这类环境问题占掉了一部分初学者大半的时间所以建议在动手写测量脚本前先跑一遍mn --test pingall用最小命令确认 Mininet 本身是健康的。5.6 端口统计的 rx_bytes 和 tx_bytes 相差明显但流量明明是对称的现象在单链路拓扑里h1 向 h2 跑 UDPs1-eth1 的tx_bytes远大于 s1-eth2 的rx_bytes。原因端口统计的单位是字节而 iperf3 的发包速率在统计周期的边缘被切割控制器两次统计请求之间并没有完全对齐另外 UDP 包的 IP 头和数据包的byte_count在 Open vSwitch 的口径里包含帧头或不包含不同版本有差异。解决不要对着单次采样下结论把 10 个周期的端口字节数差相加再除以总时长得到平均吞吐率去和 iperf3 对如果仍不一致检查是否存在从交换机 CPU 端口上送的packet_in流量这部分不计入物理端口的tx_bytes。6. 让实验从“能跑”到“像研究”测量扩展与结果验证6.1 把统计间隔缩短观测 TCP 的慢启动与拥塞避免默认 10 秒轮询适合验证功能但想测量 TCP 流的动态行为粒度太粗。我把hub.sleep(10)改成hub.sleep(1)同时让 iperf3 的-i 0.5输出半秒级速率。此时在控制器日志里可以看到流表项byte_count的增长不是线性的TCP 启动阶段每秒递增非常快随后增长变缓这正是拥塞窗口扩张和收敛的直观体现。把多轮测量的数据画成时间序列折线图就能清晰地解释“为什么 TCP 单独跑能打满带宽”。这一小节如果写进实验报告通常会得到“有数据分析意识”的评价。6.2 用 Python 进程代替 iperf3实现可控发包的测量iperf3 能制造恒定速率流但它的包大小和发送模式相对固定。为了测量交换机对不同包长的转发性能我用 Python 的socket在 Mininet 主机里写了一个 UDP 发送工具。核心代码如下# 在 h2 主机上运行python3 udp_send.py 10.0.0.1 5201 1000 import socket import sys import time dst_ip, dst_port sys.argv[1], int(sys.argv[2]) packet_size int(sys.argv[3]) # 单包字节数如 100 / 500 / 1000 packet bx * packet_size sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(b, (dst_ip, dst_port)) # 预握手让接收端提前就绪 count 0 start time.time() while time.time() - start 10: # 持续发送 10 秒 sock.sendto(packet, (dst_ip, dst_port)) count 1 print(fsent {count} packets in 10s)这段脚本的意义在于你可以通过改变packet_size和循环内的延时来控制包速率从而测量交换机的包处理能力随包长的变化规律。注意 UDP 是无连接的接收端必须提前创建 socket 并绑定端口否则主机会回 ICMP 端口不可达干扰测量结果。相比iperf3这种方式更贴近“测量实验”的科研语义控制变量、单因素分析。6.3 验证 SDN 策略的价值拔掉一条链路看流表怎么变化最后一个进阶方向是“测量配合策略验证”。用link s1 s2 down命令断开两台交换机之间的链路再让 h1 持续 ping h3。如果你的控制器实现了备用路径下发或选择重路由流表里会出现新的匹配规则指向另一条物理端口。此时对比断开前后的OFPFlowStatsReply中instructions字段以及端口统计里rx_dropped的变化就能用数据证明 SDN 控制器具备故障恢复能力。如果没有实现重路由测量的意义就退化为“观测丢包”你仍然可以从packet_in的频繁上送看到控制器的 CPU 负载飙升。这组数据我会多跑三遍拔链路前稳定 30 秒拔链路后等 10 秒恢复后再等 30 秒然后合成一张三区间的数据图。这种对比结构能同时体现测量、故障注入和策略验证三个能力是答辩与高分报告中很加分的亮点。最后提一个我自己的习惯所有轮次的结果都带上拓扑 ID 和时间戳归档宁可多存文件不要临时重新造数。希望这套流程能帮你把实验做扎实少走我当初踩过的那些弯路。本文还有配套的精品资源点击获取
