简介:这份基于SDN的园区网络检测项目源码与配套文档,面向计算机相关专业学生、教师及企业在职人员,可用于课程设计、毕业设计或项目初期演示。资源围绕软件定义网络的园区场景,提供完整可运行的检测系统,涵盖前后端代码、构建脚本及环境说明。压缩包共621个文件,包含264个Java后端文件、95个Vue前端组件、78个JavaScript脚本、87个SVG图标、13个Velocity模板及多个配置文件,便于理解模块化开发与测试流程。整体大小约1.96MB,轻量易部署,目录结构清晰。目前已有154人学习下载,具有一定参考价值。除源码外,还附带若依环境使用手册、运行与打包脚本及数据库SQL文件,读者可参照说明快速启动项目,也可在此基础上扩展功能,或作为论文撰写的实验支撑。代码经过测试运行成功,适合初学进阶与实际演练。
1. 基于SDN的园区网络检测系统:一次从控制器到管理后台的完整落地
做园区网运维的人多少都有过这种体验:核心交换机上一条广播报文异常增多,全网终端开始卡顿,可你拿着命令行逐台设备登进去看,像在黑匣子里摸象。SDN园区网络检测系统的思路是把这个黑匣子打开——由控制器统一拿交换机的实时状态,端口流量、链路通断、异常报文,全部汇聚到一个管理平台上展示和告警。这份资源正好覆盖了这条完整链路:控制器侧检测逻辑、若依框架搭的业务后台、前端可视化页面,以及配套的启动脚本和环境手册。适合拿它做毕设、课设,也适合刚接触SDN的运维人员把「控制器 + 管理平台」这条链路完整跑通,理解数据面和控制面到底是怎么协作的。
2. 系统架构与关键选型:RYU控制器加若依管理后台,这条链路怎么分工
2.1 园区网络检测为什么需要SDN:先解决可见性问题
传统园区网的检测手段主要靠SNMP轮询和端口镜像。SNMP轮询的问题在于粒度太粗,5分钟一次采样的端口流量只能看趋势,想判断某个终端是否在持续发广播帧,几乎不可能。端口镜像又受限于镜像口的数量和交换机CPU处理能力,在汇聚层设备上长期镜像不仅影响转发性能,配置起来也麻烦。
SDN的逻辑完全不同。OpenFlow协议把交换机的转发表抽象成流表,控制器可以通过标准消息拿到每个交换机、每个端口的实时统计计数,包括收发的字节数、报文数、错误包数,还能主动下流表做定向检测。这意味着园区网络里所有接入交换机的状态都汇聚到控制器这一个集中点上,检测策略也统一在控制器侧下发,不再需要逐台登录设备配ACL或镜像。
这套检测系统的定位很清楚:面向中小型园区网络,覆盖几十台OpenFlow交换机以内的规模。控制器侧用Python写的检测应用,业务侧用若依框架做管理后台,前端用Vue。这种组合在高校毕设和企业内部工具里很常见,因为每个环节都有大量现成资料可以查,遇到问题时不至于孤立无援。
2.2 控制器与业务平台的分工:数据面归RYU,管理面归若依
项目拆成两大部分:控制器侧和业务侧。控制器侧是检测能力的核心,通过OpenFlow协议和下层交换机交互,拿到拓扑、端口统计、流表匹配计数,然后把结果通过REST接口抛出去。业务侧基于若依框架,主要做用户管理、权限控制、检测规则配置、告警记录展示和数据可视化。两侧通过HTTP接口通信,控制器只负责检测和上报,不存业务数据;若依后台负责把检测结果落到MySQL里做持久化,提供给前端查询。
这种分离有个明显的好处:检测逻辑升级不影响管理平台,管理平台的权限模型也不干扰控制器的实时处理。我见过不少类似项目把检测逻辑直接写进管理后台的后端服务里,短期内开发快,但后面想换控制器或者加新的检测项,改动范围马上失控。拆开了以后,控制器API的返回格式稳定下来,后台只管解析和展示,维护边界非常清晰。
项目里目录结构也是这么对应的。后端的批处理脚本run.bat对应若依后台启动,run-web.bat对应前端Web服务,package.bat和build.bat分别对应后端打包和前端构建。.env.development是前端的环境配置,里面定义了后端API的地址,.editorconfig统一代码风格。
2.3 目录结构与bat脚本功能对照
拿到源码之后,先别急着双击脚本,看清楚每个文件是干什么的再动手。这份资源里脚本的分工大致如下:
| 脚本文件 | 对应模块 | 实际作用 |
|---|---|---|
| ry.bat | 若依后台入口 | 启动Spring Boot主程序的前置包装 |
| run.bat | 后端服务一次性启动 | 编译并启动若依后台,依赖本地的MySQL和Redis |
| run-web.bat | 前端Web服务 | 启动Vue开发服务器,默认端口通常为80或8080 |
| package.bat | 后端打包 | 执行Maven clean package,产出可部署的jar包 |
| build.bat | 前端构建 | 执行npm run build,产出dist静态资源 |
| clean.bat | 清理工作 | 清掉target目录和node_modules缓存,用于重新构建 |
| .env.development | 前端环境变量 | 配置API请求地址、端口号、代理目标 |
如果你之前跑过若依项目,对这些脚本的套路不会陌生。run.bat本质上就是mvn spring-boot:run的包装,run-web.bat是对npm run dev的包装。差别在于这个项目把两个服务拆开启动,调试时互不干扰。我一般先启动后端,观察日志输出到「启动成功」字样后,再启动前端。
提示:bat脚本里的Java路径、Maven路径和Node路径是写死的,如果你本机安装的路径不一致,需要先改脚本里的绝对路径再执行。
3. 环境搭建与一键启动:从零把后台和检测服务跑起来
3.1 前置环境版本要求
这份资源本人测试时用的是Java 8、Maven 3.6、Node 14、MySQL 5.7和Redis 5.0。这组版本组合在若依社区里是验证过最多的,不建议贸然把JDK升到17或Node升到20再跑,除非你有把握处理依赖兼容问题。如果你本机已有更高版本,我建议你在环境变量里把JAVA_HOME临时指向JDK 8再启动,避免Spring Boot版本和Maven插件之间的冲突。
MySQL和Redis必须提前装好并启动。若依后台启动后第一件事是连数据库做表结构和初始化数据校验,连不上就直接退出。MySQL里需要提前创建好数据库,编码用utf8mb4,导入项目里提供的SQL文件。如果你下载的包里没有SQL文件,可以在若依官网或项目文档里找对应版本的初始化脚本,表结构是通用的。
3.2 数据库初始化与后端启动
先创建数据库并导入初始化SQL。打开命令行,执行以下操作:
mysql -u root -p CREATE DATABASE ruoyi_sdn DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; exit; mysql -u root -p ruoyi_sdn < /path/to/sql/ruoyi.sql导入完成后,修改后端配置文件里的数据库连接信息。若依的配置在ruoyi-admin/src/main/resources/application-druid.yml里,需要把URL、用户名和密码改成你本机的实际值:
spring: datasource: druid: master: url: jdbc:mysql://localhost:3306/ruoyi_sdn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password这里有三个参数容易踩坑。useSSL=false是因为本地开发环境不需要SSL加密连接,不带这个参数在高版本MySQL驱动下会报警告或报错。zeroDateTimeBehavior=convertToNull处理的是数据库里0000-00-00类型的零日期值,不设置的话查询记录时会抛异常。serverTimezone=Asia/Shanghai解决MySQL驱动8.x版本时区偏差问题,不设置的话时间会被当成UTC时间,和本地时间差8小时。
修改完成后,双击run.bat或者手动执行:
mvn clean package -Dmaven.test.skip=true java -jar ruoyi-admin.jar看到以下日志输出时说明后台启动成功:
Started RuoYiApplication in 12.37 seconds启动过程中如果发现端口被占用,注意看8080端口是谁在占用。Windows下可以用netstat -ano | findstr :8080,查到PID后在任务管理器结束进程。
3.3 前端启动与登录验证
后端起来之后,切到前端目录。这份资源里前端代码的目录名一般是ruoyi-ui或者web,确认里面有package.json文件再执行:
npm install npm run dev如果npm install的速度很慢,用淘宝镜像源:
npm config set registry https://registry.npmmirror.com前端启动成功后会输出一个访问地址,默认是http://localhost:80,如果你本机80端口被占用了,打开ruoyi-ui/vue.config.js,把devServer.port改成一个未占用的端口:
devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }.proxy配置很关键。若依前端向后端发请求时统一走/api前缀,代理把/api开头的请求转发到后端服务的8080端口。changeOrigin必须为true,否则后端后端收到请求时Host头不匹配,部分鉴权中间件会拒绝。登录页面看到后,用默认账号admin和密码admin123登录,能正常进入仪表盘说明整个后台链路已经通了。
4. 核心检测逻辑实战:链路发现、端口统计与异常告警
4.1 控制器侧:链路发现与端口状态监控
检测系统的核心在控制器侧。项目里控制器检测应用会做两件基础事:链路发现和端口状态采集。链路发现依赖LLDP协议,控制器周期性下发LLDP探测报文,交换机收到后上报给控制器,控制器据此构建网络拓扑图。端口状态采集则是周期性地向每个交换机发送端口统计请求,拿到每个端口的收发字节和报文数。
控制器侧的关键代码逻辑大致如下,用Python伪码表达这个检测循环:
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib import dpid as dpid_lib class SdnLinkDetector(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.port_stats = {} # (dpid, port_no) -> (rx_bytes, tx_bytes) self.flow_stats = {} # dpid -> flow table entries @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def port_stats_reply_handler(self, ev): msg = ev.msg dpid = msg.datapath.id for stat in msg.body: key = (dpid, stat.port_no) old = self.port_stats.get(key) if old: rx_delta = stat.rx_bytes - old[0] tx_delta = stat.tx_bytes - old[1] # 计算当前周期的带宽利用率並上报 self._report_bandwidth(key, rx_delta, tx_delta) self.port_stats[key] = (stat.rx_bytes, stat.tx_bytes)这段代码的核心是EventOFPPortStatsReply事件处理器。控制器周期性下发OFPFlowStatsRequest和OFPPortStatsRequest,交换机回复后触发对应的事件。port_stats这个字典记录每个交换机端口的历史字节数,新数据和上一次相减就得出采样周期内的增量字节数,再除以采样间隔就能算出带宽利用率。
set_ev_cls装饰器里的MAIN_DISPATCHER参数表示只在主交换阶段处理该事件,这样避免在握手阶段收到不完整数据时误判。
4.2 业务侧:检测规则配置与告警记录落库
控制器把检测结果上报后,业务侧需要把「异常」这个概念落地成可配置的规则。若依后台的管理页面上,你可以配置每个端口的带宽告警阈值、链路通断检测间隔、广播报文比例上限等参数。配置保存后写入MySQL,后台的定时任务周期性地从控制器API拉取最新状态,和阈值做比对。
业务侧的告警判断核心逻辑如下,这里以带宽突增检测为例:
public List<AlarmRecord> checkBandwidthAlarm(List<PortStatVO> stats, List<AlarmRule> rules) { List<AlarmRecord> alarms = new ArrayList<>(); for (PortStatVO stat : stats) { for (AlarmRule rule : rules) { double ratio = stat.getBandwidthUtilization(); if (ratio >= rule.getUpperThreshold() && rule.getEnabled()) { AlarmRecord record = new AlarmRecord(); record.setSwitchDpid(stat.getDpid()); record.setPortNo(stat.getPortNo()); record.setMetricType("BANDWIDTH"); record.setCurrentValue(ratio); record.setThreshold(rule.getUpperThreshold()); record.setAlarmTime(new Date()); alarms.add(record); } } } return alarms; }这个方法的核心逻辑是两层遍历:外层遍历控制器拉取到的所有端口状态,内层遍历用户配置的告警规则。匹配到超过阈值的端口就生成一条告警记录。注意record里同时保存了当前值和阈值,这样前端展示时能直接显示超了多少,不用再回来查规则表。
4.3 把检测数据变成可视化图表
数据落到MySQL之后,前端图表就是水到渠成的事。项目前端用ECharts渲染带宽趋势图,后端提供对应的REST接口。前端请求/stat/bandwidthTrend?dpid=xxx&portNo=yyy的参数,按时间维度返回采样数据:
// 前端仪表盘组件中的核心请求逻辑 getBandwidthTrend(dpid, portNo) { return request({ url: '/stat/bandwidthTrend', method: 'get', params: { dpid: dpid, portNo: portNo, startTime: this.startTime, endTime: this.endTime } }) }这段代码直接调用封装好的request实例,baseURL在.env.development里配置为/api前缀。startTime和endTime是时间范围参数,组件里默认拉最近24小时的数据。拿到的数据是「时间点+带宽利用率」的序列,直接用ECharts的line类型渲染即可。
提示:如果你发现图表数据有断点,大概率是控制器在某几个采样周期内没有响应,比如交换机CPU繁忙或控制器负载过高。先把采样间隔调大到30秒再观察,比直接调大阈值更合理。
5. 避坑指南:从启动失败到检测不准的六个常见问题
5.1 双击bat脚本闪退
现象:Windows下双击run.bat或ry.bat时窗口一闪就消失,命令行什么都看不到。
原因:脚本里执行出错后直接exit,没有pause暂停窗口。最常见的原因是JAVA_HOME没有指向JDK 8,或者Maven路径不存在。
解决:打开命令行,手动执行脚本文件并加上pause,或者在bat的末尾自己加一行pause。也可以先执行java -version确认JDK版本是否正确,再执行mvn -version确认Maven路径。就是不要盲改脚本。
5.2 MySQL连接报Communications link failure
现象:后端启动时报com.mysql.cj.jdbc.exceptions.CommunicationsException,提示无法连接数据库。
原因:MySQL服务没启动,或者数据库名和配置里的库名不一致。也常见于MySQL做了密码策略强化,但你的连接配置依旧用旧密码格式。
解决:先确认MySQL正在运行,Windows下在服务管理器里查MySQL服务状态。再用命令行实际登录一次mysql -u root -p确认密码正确。最后检查application-druid.yml里的url里数据库名是否和CREATE DATABASE时一致。
5.3 Redis未启动或端口不一致
现象:后台日志中出现Unable to connect to Redis或JedisConnectionException,服务启动到一半就退出了。
原因:若依框架用Redis做会话缓存和验证码存储,Redis没启动时Spring容器初始化会失败。
解决:启动Redis后再重启后端。如果你的Redis设置了密码,还需要在application.yml里配置redis.password。注意若依默认情况不配密码也能连,如果你本机Redis设置了requirepass,不配置就永远连不上。
5.4 前端页面能打开但控制器状态显示离线
现象:后台登录正常,仪表盘能进去,但拓扑图和交换机列表显示离线,刷新也没变化。
原因:控制器应用没启动,或者控制器REST接口的IP端口和后台配置的不一致。前端和后端是通的,但后端到控制器的这条链路断了。
解决:先单独启动控制器应用,确认控制器的REST服务监听在哪个端口,再用浏览器直接访问控制器的健康检查接口看能否返回JSON。最后把若依后台里配置控制器地址的项改对。如果你在服务器上跑控制器,注意防火墙要放行控制器API端口,本机访问不经过防火墙,但局域网访问会被拦。
5.5 控制器能连上但检测数据始终为零
现象:控制器侧日志显示交换机已接入,但后台统计的端口流量全是0,图表是一条直线。
原因:采样周期和统计口径不一致。控制器侧可能把统计值放在某个私有字段里,而后台解析时读的是标准OpenFlow字段,两者对不上。也可以是交换机的流表没有生效,流量没有命中任何流表项,计数自然为零。
解决:先在控制器侧用命令行工具查看原始统计消息的完整内容,确认rx_bytes字段里有实际数据增长。再检查交换机的流表项,确认通配符规则存在,保证流量至少命中一条默认转发规则。如果控制器打印出来的统计值有增长但后台接口返回0,那就是JSON序列化和反序列化时字段名映射不对,仔细对比两端代码里的字段定义。
5.6 重复启动导致端口被占用
现象:第二次启动后端时报Web server failed to start. Port 8080 was already in use。
原因:上一次启动的后端进程没有完全退出,Git Bash或者IDE里杀掉终端后Java进程还在后台运行。
解决:kill掉占用8080端口的进程。Windows下执行netstat -ano | findstr :8080查看PID,然后taskkill /PID 进程号 /F。如果你用了IDE的停止按钮,注意Java进程有时不会跟着终止,需要在任务管理器里确认。
6. 再进一步:用定时任务把检测告警自动导出成日报
检测系统跑通之后,最实用的一项扩展是把每天的告警汇总成日报。若依框架自带Quartz定时任务模块,在这个基础上做导出功能不用引入任何新框架。
第一步:在若依后台新增一张告警日报表,字段包含日期、告警总数、带宽告警数、链路告警数、各交换机告警排名。第二步:写一个定时任务类,标注@Component和@Scheduled注解,每天凌晨一点执行统计查询,把昨天的告警数据按维度汇总后写入日报表。第三步:做一个导出接口,把日报表数据拼装成Excel文件返回给前端下载。
核心的定时器代码如下:
@Component public class AlarmDailyReportTask { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Autowired private AlarmRecordMapper alarmRecordMapper; @Autowired private AlarmDailyReportMapper dailyReportMapper; @Scheduled(cron = "0 0 1 * * ?") public void generateYesterdayReport() { String yesterday = LocalDate.now().minusDays(1).format(FORMATTER); List<AlarmRecord> records = alarmRecordMapper .selectListByDate(yesterday); if (records.isEmpty()) { return; } AlarmDailyReport report = new AlarmDailyReport(); report.setReportDate(yesterday); report.setTotalCount(records.size()); report.setBandwidthCount((int) records.stream() .filter(r -> "BANDWIDTH".equals(r.getMetricType())).count()); report.setLinkCount((int) records.stream() .filter(r -> "LINK".equals(r.getMetricType())).count()); dailyReportMapper.insert(report); } }cron表达式0 0 1 * * ?表示每天凌晨1点执行。selectListByDate方法按日期精准匹配,注意MySQL的DATETIME字段和Java时间字符串之间不能直接比,你需要在SQL里加上DATE_FORMAT或CAST转换。
搞定这个功能后你会意识到,检测系统真正的价值不在于实时告警,而在于数据沉淀。「昨天全网告警几次、哪个交换机占比最多」这种问题,以前要翻日志凭记忆回答,现在打开日报表一目了然。
做这类系统我有个习惯:每改一次控制器代码就要求自己重新跑一遍全链路回归——控制器起来、交换机模拟器接入、后台拉取状态、触发告警、写库、查询展示。这个过程强制走了一遍以后,再大的改动都有底气。希望这个项目的源码加上这篇笔记,能帮你把SDN园区网络检测从「看过概念」变成「跑得起来又改得动」,也希望你在这个基础上做出自己的扩展。
本文还有配套的精品资源,点击获取