240 lines
11 KiB
PowerShell
240 lines
11 KiB
PowerShell
#Requires -Version 5.1
|
||
#Requires -RunAsAdministrator
|
||
|
||
<#
|
||
.SYNOPSIS
|
||
一个智能网络监控脚本,当检测到与目标IP的网络持续中断后,会自动关闭计算机。
|
||
|
||
.DESCRIPTION
|
||
该脚本会定期测试与指定目标IP的网络连通性。
|
||
如果在设定的“监控窗口”时间内连接持续失败,脚本将启动一个可取消的关机倒计时。
|
||
如果在倒计时期间网络恢复,关机将被中止。
|
||
所有操作都会被记录在日志文件中。
|
||
#>
|
||
|
||
# ==================== 配置参数 ====================
|
||
|
||
# 监控的目标IP地址
|
||
[string]$TargetIP = "192.168.3.15"
|
||
|
||
# 正常监控时的ping间隔(秒)
|
||
[int]$NormalPingInterval = 15
|
||
|
||
# 监控窗口时长(秒) - 在此期间持续失败则触发关机
|
||
[int]$MonitorWindowSeconds = 60
|
||
|
||
# 关机倒计时时长(秒)
|
||
[int]$ShutdownCountdown = 60
|
||
|
||
# 关机倒计时阶段的ping间隔(秒)
|
||
[int]$CountdownPingInterval = 3
|
||
|
||
# ping超时时间(秒)
|
||
[int]$PingTimeout = 3
|
||
|
||
# 日志配置
|
||
[string]$LogDirectory = Join-Path -Path $PSScriptRoot -ChildPath "logs"
|
||
#[string]$LogFile = Join-Path -Path $LogDirectory -ChildPath "network_monitor_$(Get-Date -Format 'yyyyMMdd').log"
|
||
[int]$MaxLogDays = 30
|
||
|
||
# =================================================
|
||
|
||
# --- 函数定义 ---
|
||
|
||
# 日志写入函数
|
||
function Write-Log {
|
||
param(
|
||
[Parameter(Mandatory=$true)]
|
||
[ValidateSet("INFO", "SUCCESS", "FAIL", "WARN", "CRITICAL", "DEBUG")]
|
||
[string]$Level,
|
||
|
||
[Parameter(Mandatory=$true)]
|
||
[string]$Message
|
||
)
|
||
|
||
# ===================== V0.2update =====================
|
||
# 每次写日志时,都重新根据当前日期确定日志文件名
|
||
$LogFile = Join-Path -Path $LogDirectory -ChildPath "network_monitor_$(Get-Date -Format 'yyyyMMdd').log"
|
||
# ====================================================
|
||
|
||
$logTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||
$logEntry = "[$logTimestamp] [$Level] $Message"
|
||
|
||
# 尝试写入日志文件
|
||
try {
|
||
if (-not (Test-Path -Path $LogDirectory)) {
|
||
New-Item -Path $LogDirectory -ItemType Directory -ErrorAction Stop | Out-Null
|
||
}
|
||
Add-Content -Path $LogFile -Value $logEntry -ErrorAction Stop
|
||
}
|
||
catch {
|
||
Write-Host "[$logTimestamp] [CRITICAL] 日志文件写入失败!请检查权限或磁盘空间。" -ForegroundColor Red
|
||
}
|
||
|
||
# 根据日志级别在控制台输出不同颜色的信息
|
||
switch ($Level) {
|
||
"SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
|
||
"FAIL" { Write-Host $logEntry -ForegroundColor Red }
|
||
"WARN" { Write-Host $logEntry -ForegroundColor Yellow }
|
||
"CRITICAL" { Write-Host $logEntry -ForegroundColor DarkRed }
|
||
default { Write-Host $logEntry }
|
||
}
|
||
}
|
||
|
||
# 获取本机的最佳IP地址
|
||
function Get-LocalIP {
|
||
param($TargetIP)
|
||
try {
|
||
# 尝试寻找与目标IP在同一个子网的本机IP地址
|
||
$targetSubnet = ($TargetIP.Split('.')[0..2]) -join '.'
|
||
$ip = Get-NetIPAddress -AddressFamily IPv4 -AddressState Preferred | Where-Object { $_.IPAddress -like "$targetSubnet.*" } | Select-Object -First 1 -ExpandProperty IPAddress
|
||
|
||
if ($ip) { return $ip }
|
||
|
||
# 如果找不到,则返回任意一个非环回的私有IP地址
|
||
$ip = Get-NetIPAddress -AddressFamily IPv4 -AddressState Preferred | Where-Object { $_.IPAddress -notlike "127.0.0.1" -and $_.InterfaceAlias -notlike "Loopback*" } | Select-Object -First 1 -ExpandProperty IPAddress
|
||
return $ip
|
||
}
|
||
catch {
|
||
return "未知"
|
||
}
|
||
}
|
||
|
||
|
||
# --- 脚本主逻辑 ---
|
||
|
||
# 清理旧日志
|
||
if (Test-Path -Path $LogDirectory) {
|
||
Get-ChildItem -Path $LogDirectory -Filter "*.log" | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$MaxLogDays) } | Remove-Item
|
||
}
|
||
|
||
# 初始化
|
||
Clear-Host
|
||
$localIP = Get-LocalIP -TargetIP $TargetIP
|
||
Write-Log -Level "INFO" -Message "========== 脚本启动 =========="
|
||
Write-Log -Level "INFO" -Message "本机IP: $localIP"
|
||
Write-Log -Level "INFO" -Message "监控目标: $TargetIP"
|
||
Write-Log -Level "INFO" -Message "监控窗口: ${MonitorWindowSeconds}秒"
|
||
Write-Log -Level "INFO" -Message "关机倒计时: ${ShutdownCountdown}秒"
|
||
Write-Log -Level "INFO" -Message "日志文件: $LogFile"
|
||
Write-Log -Level "INFO" -Message "脚本已启动,按 Ctrl+C 退出..."
|
||
Write-Host "========================================"
|
||
|
||
# 主循环
|
||
$failureStartTime = $null
|
||
$normalStatusCounter = 0 # 正常状态计数器,用于定期记录正常运行日志
|
||
$normalStatusLogInterval = 24 # 每24次正常检测记录一次日志(约6分钟)
|
||
|
||
while ($true) {# 测试网络连接
|
||
try {
|
||
# 使用兼容性更好的ping方法
|
||
$pingResult = Test-Connection -ComputerName $TargetIP -Count 1 -Quiet -ErrorAction SilentlyContinue
|
||
$isOnline = $pingResult
|
||
} catch {
|
||
Write-Log -Level "FAIL" -Message "网络测试出现异常: $($_.Exception.Message)"
|
||
$isOnline = $false
|
||
} if ($isOnline) {
|
||
if ($failureStartTime) {
|
||
Write-Log -Level "SUCCESS" -Message "网络连接已恢复。"
|
||
Write-Host "[OK] 网络连接已恢复,重置监控窗口" -ForegroundColor Green
|
||
$failureStartTime = $null # 重置失败计时器
|
||
$normalStatusCounter = 0 # 重置正常状态计数器
|
||
} else {
|
||
Write-Host "[OK] 网络连接正常" -ForegroundColor Green
|
||
# 正常连接时减少日志频率,只定期记录
|
||
$normalStatusCounter++
|
||
if ($normalStatusCounter -ge $normalStatusLogInterval) {
|
||
Write-Log -Level "INFO" -Message "网络连接持续正常(已检测 $normalStatusCounter 次)"
|
||
$normalStatusCounter = 0 # 重置计数器
|
||
}
|
||
}
|
||
# 只在控制台显示等待信息,不写入日志文件
|
||
Write-Host "等待 ${NormalPingInterval}秒后继续监控..." -ForegroundColor Cyan
|
||
Start-Sleep -Seconds $NormalPingInterval
|
||
|
||
} else {
|
||
# 如果网络不通
|
||
Write-Log -Level "FAIL" -Message "Ping失败 - 目标: $TargetIP"
|
||
|
||
if (-not $failureStartTime) {
|
||
# 记录首次失败的时间
|
||
$failureStartTime = Get-Date
|
||
Write-Log -Level "WARN" -Message "网络首次中断,开始进入监控窗口计时。"
|
||
} # 计算网络已中断的时间
|
||
$failureDuration = (New-TimeSpan -Start $failureStartTime -End (Get-Date)).TotalSeconds
|
||
Write-Log -Level "INFO" -Message "网络已持续中断 $([math]::Round($failureDuration)) / ${MonitorWindowSeconds} 秒。"
|
||
|
||
# 显示详细状态
|
||
$remainingTime = $MonitorWindowSeconds - [math]::Round($failureDuration)
|
||
Write-Host "监控状态 - 已中断: $([math]::Round($failureDuration))/${MonitorWindowSeconds}秒,剩余: ${remainingTime}秒" -ForegroundColor Yellow
|
||
|
||
if ($failureDuration -ge $MonitorWindowSeconds) {
|
||
# 持续失败时间超过监控窗口,开始关机倒计时
|
||
Write-Log -Level "CRITICAL" -Message "网络持续中断已超过 ${MonitorWindowSeconds} 秒,开始关机流程。"
|
||
Write-Host "========================================" -ForegroundColor Yellow
|
||
$shutdownCancelled = $false
|
||
$countdownEndTime = (Get-Date).AddSeconds($ShutdownCountdown)
|
||
while ((Get-Date) -lt $countdownEndTime -and -not $shutdownCancelled) {
|
||
$remainingSeconds = [math]::Ceiling(($countdownEndTime - (Get-Date)).TotalSeconds)
|
||
if ($remainingSeconds -le 0) { break }
|
||
|
||
Write-Log -Level "WARN" -Message "距离关机还有 $remainingSeconds 秒... 正在快速检测网络。"
|
||
Write-Host "----------------------------------------" -ForegroundColor Yellow
|
||
Write-Host "距离关机还有 $remainingSeconds 秒..." -ForegroundColor Yellow
|
||
Write-Host "正在快速检测网络连接... 按任意键可手动取消关机" -ForegroundColor Yellow # 在倒计时中再次检测网络
|
||
try {
|
||
if (Test-Connection -ComputerName $TargetIP -Count 1 -Quiet -ErrorAction SilentlyContinue) {
|
||
Write-Log -Level "SUCCESS" -Message "网络在倒计时期间恢复!取消关机。"
|
||
Write-Host "========================================" -ForegroundColor Green
|
||
Write-Host "[OK] 网络连接已恢复!取消关机操作" -ForegroundColor Green
|
||
Write-Host "========================================" -ForegroundColor Green
|
||
$failureStartTime = $null
|
||
$shutdownCancelled = $true
|
||
break
|
||
} else {
|
||
Write-Host "[X] 网络仍然中断" -ForegroundColor Red
|
||
}
|
||
}
|
||
catch {
|
||
Write-Log -Level "WARN" -Message "倒计时期间网络测试出现异常: $($_.Exception.Message)"
|
||
Write-Host "[X] 网络测试异常" -ForegroundColor Red
|
||
}
|
||
# 检查是否有按键输入(手动取消)
|
||
$timeout = $CountdownPingInterval
|
||
$startTime = Get-Date
|
||
while (((Get-Date) - $startTime).TotalSeconds -lt $timeout -and -not $shutdownCancelled) {
|
||
if ([Console]::KeyAvailable) {
|
||
$null = [Console]::ReadKey($true) # 读取按键但不使用
|
||
Write-Log -Level "WARN" -Message "用户手动取消了关机操作" Write-Host ""
|
||
Write-Host "========================================" -ForegroundColor Cyan
|
||
Write-Host "[INFO] 用户已手动取消关机,返回监控模式" -ForegroundColor Cyan
|
||
Write-Host "========================================" -ForegroundColor Cyan
|
||
$failureStartTime = $null
|
||
$shutdownCancelled = $true
|
||
break }
|
||
Start-Sleep -Milliseconds 100
|
||
}
|
||
}
|
||
|
||
if (-not $shutdownCancelled) {
|
||
Write-Log -Level "CRITICAL" -Message "关机倒计时完成,执行系统关机命令。"
|
||
try {
|
||
# 取消注释下一行以启用实际关机
|
||
# Stop-Computer -Force
|
||
Write-Host "模拟关机:Stop-Computer -Force (为防止意外,此行已注释)" -ForegroundColor Red
|
||
Write-Log -Level "CRITICAL" -Message "关机命令已执行(模拟模式)。"
|
||
}
|
||
catch {
|
||
Write-Log -Level "CRITICAL" -Message "关机命令执行失败: $($_.Exception.Message)"
|
||
}
|
||
# 脚本执行完关机命令后可以退出
|
||
exit
|
||
}
|
||
} else {
|
||
# 在监控窗口期内,继续等待
|
||
Write-Log -Level "INFO" -Message "等待 ${NormalPingInterval}秒后继续..."
|
||
Start-Sleep -Seconds $NormalPingInterval
|
||
}
|
||
}
|
||
}
|