最近几天,我一直泡在客户现场,为一套出入管理系统做安装和调试。项目本身并不算特别复杂,但网络链路略长、设备层级较多,再叠加门禁前端设备对网络稳定性的敏感度,几乎把这个“小工程”变成了一次完整的网络排障实战。
在这个过程中,我发现仅靠临时敲几下 ping 命令远远不够:需要能够长期监控、按时间统计丢包、自动记录日志的“小工具”,帮我判断网络到底是偶发抖动,还是结构性不稳定。于是,就有了本文分享这个已经跑通、用着还不错的 BAT 监控小脚本。希望对你也有用~
1. 当前项目的网络拓扑结构
本次项目的系统依托园区现有网络架构,通过多级接入交换机构成一条从服务器到岗亭电脑、再到门禁前端设备的完整链路。整体设计看似规范,但只要其中某个环节出现问题,尤其是接入交换机 1 不稳定,后续所有设备便会受到连带影响。总体而言,这套网络结构属于典型的“业务分层 + VLAN 分区”模式:
- 下层是门禁专网和办公终端网段
- 上层是园区核心网络和服务器资源
- 中间是三层/二层交换机级联
从后端服务器到岗亭电脑,大致链路如下:
Server 172.20.1.35
│
▼
Core Network (Core Switch / Router / ...)
│
▼
Access Switch 1 ── 10.255.18.21
│
▼
Access Switch 2 ── 10.255.18.23
│
▼
Access Switch 3 ── 10.255.18.24
│
├── VLAN 107 (172.18.7.0/24)
│ │
│ ▼
│ 岗亭电脑 ── 172.18.7.90
│
└── VLAN 121 (192.168.121.0/24)
│
├── 车牌识别一体机...
│
└── 人脸识别一体机...
2. 问题背景 & 原因
我们最初发现前端的车牌识别一体机频繁无故掉线,起初以为是供电不稳定,但进一步排查后才确认问题出在网络链路上。在服务器端持续 ping 相关设备时,可以明显看到偶发的连续掉包。经过几轮基础测试,我们最终发现:从服务器到第一台接入交换机的链路本身就存在不稳定。既然最前端链路已经开始丢包,那么后续所有依赖这条链路的设备,其网络质量只会在此基础上持续衰减,绝不可能更好…😅
对客户而言,这种情况并不是“偶发的小波动”,而是会直接影响现场体验、业务流程以及系统可信度的核心问题。因此,我需要一种更专业、更量化的方法,来准确证明这条链路的“不稳定性”,并提供可追溯的数据依据。
当然,目前仍无法完全判定导致本次网络故障的根本原因……如果各位大佬有类似经验,也请不吝分享,感激不尽…
3. 自制网络监控脚本
一直盯着命令行窗口看 ping 输出既不现实也不专业,因此我干脆写了一个小工具,用 Windows 批处理脚本做了一套“简易 Ping 监控系统”。它不华丽,但非常实用。🛠️
3.1 工具设计目标 🎯
在写这个 BAT 的时候,我给自己定了几个简单但刚好够用的目标:
- 能够长时间稳定运行,不需要人为值守
- 支持用户自定义目标 IP,并且有默认值(当前网关,实在取不到就用 8.8.8.8)
- 对用户输入的 IPv4 做基础校验,避免输错 IP 浪费时间
- 每秒 ping 一次,按小时做统计汇总:总次数、超时次数、丢包率
- 自动把小时统计结果写入日志文件,方便回看历史情况
- 按 Ctrl + C 时可以正常结束,由系统提示按 Y 退出、按 N 继续,让行为可控
简而言之,就是做一个“比裸 ping 更聪明一点”的小监控工具。
3.2 代码核心亮点 💡
虽然整个 BAT 文件有点长,但逻辑并不复杂,可以拆成几个关键点来理解。
1)自动获取默认网关,作为默认监测目标
脚本启动时,会尝试通过 route print 和 wmic 等方式获取当前网络默认网关,获取不到再回退到 8.8.8.8:
- 优先用
Get-NetRoute查0.0.0.0/0的 NextHop - 如果失败,则用
route print解析默认路由 - 再不行就用
wmic nicconfig取DefaultIPGateway - 最终兜底:
TARGET_DEFAULT=8.8.8.8
这样,在大多数现场环境里,哪怕用户不输入任何 IP,直接回车也能用“当前网关”来做监控,非常契合调试场景。

2)用户输入 + IPv4 合法性校验
为了避免输错 IP,脚本对输入做了简单的 IPv4 校验:
- 必须是四段,用点分隔
- 每一段必须是 0–255 之间的纯数字
- 最多允许三次错误,超过次数就直接退出,避免误操作
3)按小时统计丢包并写入日志
脚本内部维护了几个计数器:total、timeout 等,每 ping 一次就累加一次总数,超时就累加一次超时数,并根据当前小时与上一轮比较,一旦跨小时就自动结算上一小时的统计结果。日志文件按日期和目标 IP 命名,例如:C:\ping_logs\Ping_10.255.18.21_20251121.log,这对回溯某一天、某一小时是否出现过明显丢包非常有帮助。最终的脚本如下,你可以另存为 PingLogger.bat 文件,或者从下面下载。


@echo off
setlocal EnableDelayedExpansion
:: ==================== Init default gateway as TARGET_DEFAULT ====================
call :GetDefaultGateway
:: ==================== Intro ====================
cls
echo Ping monitor utility
echo - Send one ping per second to target IP
echo - Show hourly packet loss summary on screen and in log file
echo - Log files are saved under C:\ping_logs
echo.
echo Copyright (c) Kevin's, www.shephe.com
echo.
:: ==================== Ask user for target IP ====================
call :AskTargetIP
if %errorlevel% geq 1 goto :EOF
:: ==================== Main config ====================
set "LOGDIR=C:\ping_logs"
:: Create folder if needed
if not exist "%LOGDIR%" md "%LOGDIR%" >nul 2>&1
:: Get today and current hour via PowerShell (robust, no wmic needed)
for /f "delims=" %%a in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd" 2^>nul') do set "TODAY=%%a"
for /f "delims=" %%a in ('powershell -NoProfile -Command "Get-Date -Format HH" 2^>nul') do set "CURRENT_HOUR=%%a"
if not defined TODAY (
echo Error: Could not get date. Using fallback.
for /f "tokens=2 delims==" %%i in ('wmic os get localdatetime /value 2^>nul') do set dt=%%i
if defined dt (
set TODAY=!dt:~0,4!!dt:~4,2!!dt:~6,2!
) else (
set TODAY=%date:~10,4%%date:~4,2%%date:~7,2%
)
)
if not defined CURRENT_HOUR set CURRENT_HOUR=%time:~0,2%
set "LOGFILE=%LOGDIR%\Ping_%TARGET%_%TODAY%.log"
set total=0
set timeout=0
cls
echo.
echo ==========================================================
echo Monitoring %TARGET% (hourly loss summary)
echo Log file: %LOGFILE%
echo Press Ctrl+C to stop anytime
echo ==========================================================
echo.
echo [%TODAY% %time:~0,8%] Monitoring started for %TARGET% >> "%LOGFILE%"
:loop
set /a total=total+1
:: Ping once, 1-sec timeout
ping -n 1 -w 1000 %TARGET% >nul 2>&1
if errorlevel 1 (
set /a timeout=timeout+1
set MARK=X
) else (
set MARK=.
)
:: Live display (break line every 60 pings to avoid spam)
set /a mod=total %% 60
if !mod!==0 (
<nul set /p ="!MARK! (!total!/!timeout!) "
echo.
) else (
<nul set /p ="!MARK! "
)
:: Refresh current hour
for /f "delims=" %%h in ('powershell -NoProfile -Command "Get-Date -Format HH" 2^>nul') do set "THIS_HOUR=%%h"
if not defined THIS_HOUR set THIS_HOUR=%time:~0,2%
:: Hour change? Summarize previous hour
if not "!THIS_HOUR!"=="!CURRENT_HOUR!" (
if !total! gtr 0 (
set /a loss_rate=timeout*100/total
) else (
set loss_rate=0
)
set summary=[%TODAY% !CURRENT_HOUR!:00-!CURRENT_HOUR!:59] Total:!total! Timeout:!timeout! Loss:!loss_rate!%%
echo.
echo -----------------------------------------------------------
echo !summary!
echo -----------------------------------------------------------
echo !summary! >> "%LOGFILE%"
:: Midnight? Update for new day
if "!CURRENT_HOUR!"=="23" if "!THIS_HOUR!"=="00" (
:: Get new today
for /f "delims=" %%a in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd" 2^>nul') do set "NEW_TODAY=%%a"
if not defined NEW_TODAY (
for /f "tokens=2 delims==" %%i in ('wmic os get localdatetime /value 2^>nul') do set dt=%%i
if defined dt (
set NEW_TODAY=!dt:~0,4!!dt:~4,2!!dt:~6,2!
) else (
set NEW_TODAY=%date:~10,4%%date:~4,2%%date:~7,2%
)
)
set TODAY=!NEW_TODAY!
set "LOGFILE=%LOGDIR%\Ping_%TARGET%_%TODAY%.log"
echo [%TODAY% %time:~0,8%] Monitoring started for %TARGET% >> "%LOGFILE%"
)
:: Reset
set total=0
set timeout=0
set CURRENT_HOUR=!THIS_HOUR!
)
:: ~1 second delay (timeout preferred, fallback to ping)
timeout /t 1 /nobreak >nul 2>&1
if errorlevel 1 ping -n 2 127.0.0.1 >nul 2>&1
goto loop
goto :EOF
:: ==========================================================
:: Subroutine: GetDefaultGateway -> TARGET_DEFAULT
:: Try PowerShell, then route print, then WMIC, fallback 8.8.8.8
:: ==========================================================
:GetDefaultGateway
set "TARGET_DEFAULT="
:: Try PowerShell Get-NetRoute (newer systems)
for /f "usebackq tokens=* delims=" %%g in (`
powershell -NoProfile -Command "(Get-NetRoute -DestinationPrefix '0.0.0.0/0' ^| Sort-Object RouteMetric ^| Select-Object -First 1).NextHop" 2^>nul
`) do (
if not "%%g"=="" set "TARGET_DEFAULT=%%g"
)
:: Fallback: parse 'route print' default route (works on most Windows)
if not defined TARGET_DEFAULT (
for /f "tokens=1-3" %%a in ('route print ^| findstr "0.0.0.0 0.0.0.0"') do (
if not defined TARGET_DEFAULT (
if "%%a"=="0.0.0.0" if "%%b"=="0.0.0.0" set "TARGET_DEFAULT=%%c"
)
)
)
:: Fallback: WMIC default gateway
if not defined TARGET_DEFAULT (
set "gw="
for /f "tokens=2 delims==" %%g in ('wmic nicconfig where "IPEnabled=TRUE and DefaultIPGateway is not null" get DefaultIPGateway /value 2^>nul ^| find "="') do (
set "gw=%%g"
)
if defined gw (
set "gw=!gw:{=!"
set "gw=!gw:}=!"
for /f "tokens=1 delims=," %%x in ("!gw!") do set "TARGET_DEFAULT=%%x"
)
)
:: Final fallback: use 8.8.8.8
if not defined TARGET_DEFAULT set "TARGET_DEFAULT=8.8.8.8"
exit /b
:: ==========================================================
:: Subroutine: AskTargetIP -> TARGET
:: Validate IPv4, allow 3 attempts total
:: ==========================================================
:AskTargetIP
set "attempt=0"
:ask_loop
set /a attempt+=1
echo.
set /p "TARGET=Please enter target IPv4 address (default: %TARGET_DEFAULT%): "
if "%TARGET%"=="" set "TARGET=%TARGET_DEFAULT%"
call :ValidateIPv4 "%TARGET%" valid
if /i "%valid%"=="true" (
echo Target IP accepted: %TARGET%
exit /b 0
) else (
echo Invalid IPv4 address: %TARGET%
if %attempt% GEQ 3 (
echo Too many invalid attempts. Exiting.
exit /b 1
) else (
echo Please try again.
goto ask_loop
)
)
:: ==========================================================
:: Subroutine: ValidateIPv4 "%1" -> result in %2 (true/false)
:: ==========================================================
:ValidateIPv4
setlocal EnableDelayedExpansion
set "ip=%~1"
set "ok=true"
set "o1="
set "o2="
set "o3="
set "o4="
for /f "tokens=1-4 delims=." %%a in ("%ip%") do (
set "o1=%%a"
set "o2=%%b"
set "o3=%%c"
set "o4=%%d"
)
:: Must have four octets
if not defined o1 set "ok=false"
if not defined o2 set "ok=false"
if not defined o3 set "ok=false"
if not defined o4 set "ok=false"
:: Rebuild address to ensure exactly 3 dots and no extra text
if "!ok!"=="true" (
set "rebuild=!o1!.!o2!.!o3!.!o4!"
if /i not "!rebuild!"=="!ip!" set "ok=false"
)
:: Check numeric and range 0-255 for each octet
if "!ok!"=="true" (
for %%O in (!o1! !o2! !o3! !o4!) do (
if "%%O"=="" set "ok=false"
for /f "delims=0123456789" %%Z in ("%%O") do (
if not "%%Z"=="" set "ok=false"
)
if "!ok!"=="true" (
if %%O LSS 0 set "ok=false"
if %%O GTR 255 set "ok=false"
)
)
)
endlocal & set "%2=%ok%"
exit /b
3.3 实战中的使用方式 🧪
在这次门禁项目中,我主要是这样用这个脚本的:
- 在服务器上,以接入交换机 1(10.255.18.21)为目标跑长期监控,观察核心到接入层的稳定性
- 必要时,把目标改成接入交换机 2、3,甚至是岗亭电脑 IP,用来判断问题到底出在哪个“段落”
- 结合日志中的小时丢包率,能看到是否有某些时间段(比如工作高峰、夜间备份时)丢包特别明显
相比肉眼盯着 ping 窗口,这个小工具的优势在于:
- 可以丢给客户或值班人员,让他们开着窗口就行
- 只要事后拿到日志,就能快速定位“问题大概在什么时候开始、持续了多久”
4. 解决方案和优化思路
有了监控数据,后面的工作就从“凭感觉猜”变成了“拿数据说话”。以Ping_172.20.1.33_20251121.log为例,可以从日志直观地分析出时间段内两个设备之间的网络质量情况。
[20251121 15:15:35] Monitoring started for 172.20.1.33
[20251121 15:00-15:59] Total:2051 Timeout:615 Loss:29%
[20251121 16:00-16:59] Total:3417 Timeout:183 Loss:5%
[20251121 17:00-17:59] Total:3538 Timeout:62 Loss:1%
[20251121 18:00-18:59] Total:3528 Timeout:71 Loss:2%
[20251121 19:00-19:59] Total:3567 Timeout:33 Loss:0%
4.1 硬件与物理链路排查 🔍
针对接入交换机 1 到后续设备这段链路,我会优先检查:
- 上联光模块、网线、水晶头是否存在接触不良、氧化或质量问题
- 交换机端口是否有错误包统计(如 CRC、input error 等,需在设备上查看)
- 端口速率、双工模式是否一致,是否有“协商不一致”导致性能抖动
对于弱电工程而言,物理层的小故障往往最“隐蔽”、也最容易被忽视……遗憾的是,即使我们心里很清楚问题通常就藏在这些基础环节里,却依然难以完全避免。更无奈的是,绝大多数此类问题最终都指向同一个根源——施工不规范。
4.2 配置与拓扑优化 🧩
在排除物理问题之后,若还未解决故障,那么重点就回到配置和拓扑本身:
- 检查 trunk 口 VLAN 允许列表是否正确,避免不必要的广播、组播泛滥
- 查看是否存在冗余链路却没有正确启用生成树(STP),防止广播风暴
- 合理规划 VLAN 之间的三层转发路径,保证门禁专网数据不过多绕路
- 在条件允许的情况下,适度“拉近”服务器与门禁专网之间的网络距离,减少不必要的跳数
这些工作都可以结合 BAT 工具的监控结果,一边调、一边看丢包和延迟是否明显改善...
4.3 持续监控与后续计划 📊
这次写出来的 BAT 小工具,本来只是为了应付当前项目,但实际用下来体验不错,也让我意识到:
- 对于类似门禁、监控、楼宇自控这类弱电系统,网络质量就是“生命线”
- 一个轻量级的监控脚本,比起直接上大而全的监控平台,反而更适合在现场快速部署
- 未来可以继续完善,比如加一个简单的图形展示(配合日志导出到其他工具)、或者加入多目标轮询等功能
接下来我也会把这次的脚本和调试过程整理好,发布在自己的博客 Kevin's Space 上,既是给自己做个备忘,也是给后来者多提供一个排障思路。不得不说,AI 真好啊~
网络故障解决的确比较麻烦。