网络测速脚本(MacOS和Linux平台可用)

文章目录

核心目标

本脚本旨在提供一个轻量级、跨平台、实时可视化的网络质量监控解决方案,核心解决以下问题:

  1. 多维度质量评估:传统工具仅测连通性,本脚本结合 Ping(延迟、丢包率)与 Curl(实际下载速率),全面反映用户体验。
  2. 跨平台兼容性 :自动识别 macOS (Darwin) 与主流 Linux 发行版(CentOS/Ubuntu),屏蔽 ping 超时参数单位等底层差异,实现"一次编写,到处运行"。
  3. 实时网络波动:采用终端原地刷新机制,以固定频率(默认 5 秒)更新看板,直观展示实时波动。
  4. 直观状态判断:基于预设阈值将延迟/网速自动划分为"优秀、良好、一般、较差、极差"五个等级,通过颜色编码(绿/黄/红)快速定位瓶颈。
  5. 高效故障定位:自动统计失败站点列表,帮助运维/开发者迅速判断是局部服务不可用还是整体网络中断。

脚本功能详解

该脚本 (network-monitor.sh) 集成以下关键功能模块:

混合探测机制 (Ping + Curl)
  • Ping 探测
    • 使用 ping -c 1 单次快速探测,减少等待时间。
    • 智能解析 min/avg/max 往返时间统计行,提取平均延迟。
    • 计算丢包率,若丢包率 100% 则直接判定超时,跳过后续测速以节省资源。
  • Curl 测速
    • 仅对 Ping 成功的站点执行,避免在不可达主机上浪费超时时间。
    • 使用 curl -L 自动跟随 HTTP 301/302 跳转,确保测试最终落地页(如 www.baidu.com 跳转至 HTTPS 首页)。
    • 利用 -%{speed_download} 获取实时下载速度(字节/秒),转换为人类可读的 KB/s
    • 设置严格总超时(默认 3 秒),防止慢速响应阻塞主循环。
自适应阈值评级系统

内置两套独立评级标准,分别针对延迟带宽

  • 延迟评级
    • 🟢 优秀:< 50ms (适合实时游戏、高频交易)
    • 🟢 良好:50-100ms (适合视频会议、流畅浏览)
    • 🟡 一般:100-200ms (普通网页浏览可接受)
    • 🔴 较差:200-500ms (明显卡顿)
    • 🔴 极差:> 500ms (几乎不可用)
  • 网速评级 (基于 KB/s):
    • 🟢 优秀:> 250 KB/s (>2Mbps,加载大图/视频流畅)
    • 🟢 良好:150-250 KB/s
    • 🟡 一般:60-150 KB/s (纯文本/小图正常)
    • 🔴 较差:20-60 KB/s
    • 🔴 极差:< 20 KB/s
精确的时间控制循环
  • 解决传统 sleep 导致的累积延迟问题。
  • 逻辑:睡眠时间 = 设定间隔 - (当前时间 - 循环开始时间)
  • 即使某个站点探测耗时较长,也能保证下一次刷新贴近设定时间点(如每 5 秒整),保持监控节奏稳定。
  • 首次运行立即执行,无初始等待。
依赖自检与容错
  • 启动时自动检查 bc (计算器) 是否安装,未安装时给出针对 macOS (brew) 和 Linux (apt/yum) 的具体安装指令。
  • 正则表达式校验 curl 返回的速度值,防止非数字字符导致脚本崩溃。
  • 处理 ping 输出格式在 macOS 和 Linux 下的细微差异。

脚本代码

bash 复制代码
#!/bin/bash

# ==============================
# 实时网络质量监控脚本 (network-monitor.sh)
# 兼容:macOS (Darwin) + Linux (CentOS/Ubuntu)
# 功能:
#   - Ping 测试:评估连通性与延迟
#   - Curl 测试:对可达站点测速(KB/s),自动跟随 HTTP 跳转(-L)
# 用法:chmod +x network-monitor.sh && ./network-monitor.sh
# 退出:按 Ctrl+C
# ==============================

# ┌──────────────────────────────┐
# │        可配置区域(请在此修改)       │
# └──────────────────────────────┘

# 刷新间隔(秒)
REFRESH_INTERVAL=5

# 监控站点列表(格式:"地址|说明")
SITES=(
    "www.baidu.com|百度首页"
    "www.tencent.com|腾讯首页"
    "www.taobao.com|淘宝首页"
    "www.douyin.com|抖音首页"
    "doubao.com|豆包AI"
    "qianwen.com|千问AI"
    "yuanbao.tencent.com|元宝AI"
#    "192.0.2.1|失败测试"
)

# ===== Ping 延迟阈值(单位:毫秒)=====
EXCELLENT_THRESHOLD=50      # 优秀
GOOD_THRESHOLD=100          # 良好
FAIR_THRESHOLD=200          # 一般
POOR_THRESHOLD=500          # 较差

# ===== Curl 网速阈值(单位:KB/s)=====
SPEED_EXCELLENT=250         # 优秀(>250 KB/s,对应淘宝257.9、腾讯382.6等峰值站点)
SPEED_GOOD=150              # 良好(150~250 KB/s,对应抖音157.7、豆包199.4、腾讯176.9等)
SPEED_FAIR=60               # 一般(60~150 KB/s,对应百度83.8、淘宝128.7、千问75.7等主流站点)
SPEED_POOR=20               # 较差(20~60 KB/s,对应百度41.6、元宝43.7等低值站点)
# <20 KB/s 视为极差(对应抖音10.5 KB/s等最低值)

# Ping 参数
PING_COUNT=1                # 每个站点 ping 次数
PING_TIMEOUT_SEC=1          # 单个 ping 包超时(秒)

# Curl 网速测试超时(秒),仅用于 curl -m
CURL_TIMEOUT_SEC=3          # 不在界面展示,仅内部使用

# ┌──────────────────────────────┐
# │           通用配置             │
# └──────────────────────────────┘

# 跨平台 Ping 超时参数
if [[ "$OSTYPE" == "darwin"* ]]; then
    PING_TIMEOUT_MS=$(echo "scale=0; $PING_TIMEOUT_SEC * 1000" | bc -l)
    PING_W_PARAM="-W $PING_TIMEOUT_MS"
else
    PING_W_PARAM="-W $PING_TIMEOUT_SEC"
fi

# 颜色定义
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
CYAN=$'\033[0;36m'
NC=$'\033[0m' # No Color

# 检查依赖
check_dependencies() {
    if ! command -v bc &> /dev/null; then
        if [[ "$OSTYPE" == "darwin"* ]]; then
            echo "❌ 未安装 bc,请执行:brew install bc"
        else
            echo "❌ 未安装 bc,请执行:sudo apt install bc 或 yum install bc"
        fi
        exit 1
    fi
}

# ┌──────────────────────────────┐
# │  核心函数:测试单个站点(含 Ping + Curl) │
# └──────────────────────────────┘
test_site() {
    local entry="$1"
    IFS='|' read -r site desc <<< "$entry"

    local output ping_exit_code packet_loss loss_percent success_count
    local stats_line numbers avg avg_formatted result=""
    local is_success=0

    # 执行 Ping
    LC_ALL=C
    output=$(LC_ALL=C ping -c "$PING_COUNT" $PING_W_PARAM "$site" 2>&1)
    ping_exit_code=$?

    # 解析丢包率
    packet_loss=$(echo "$output" | grep -Eo '[0-9.]+% packet loss' | awk '{print $1}' | tr -d '%')
    if [ -n "$packet_loss" ]; then
        loss_percent=$(echo "$packet_loss" | bc -l)
        success_count=$(echo "scale=0; $PING_COUNT * (100 - $loss_percent) / 100" | bc -l)
    else
        if [ "$ping_exit_code" -eq 0 ]; then
            success_count=$PING_COUNT
        else
            success_count=0
        fi
    fi

    # 初始化网速相关变量
    local speed_kbps=0
    local speed_status=""

    if [ "$success_count" -gt 0 ]; then
        # 提取延迟统计
        if [[ "$OSTYPE" == "darwin"* ]]; then
            stats_line=$(echo "$output" | grep "round-trip" | head -n1)
        else
            stats_line=$(echo "$output" | grep -E "(min/avg/max|rtt)" | head -n1)
        fi

        if [ -n "$stats_line" ]; then
            numbers=$(echo "$stats_line" | grep -o '[0-9.]\+/[0-9.]\+/[0-9.]\+' | head -n1)
            if [ -n "$numbers" ]; then
                IFS='/' read -r min avg max <<< "$numbers"
                avg_formatted=$(printf "%.1f" "$avg")
                is_success=1
            fi
        fi

        # === 对 Ping 成功的站点进行网速测试 ===
        # 使用 http:// 触发跳转,-L 自动跟随 HTTP 301/302
        local curl_url="http://$site"
        local curl_result
        # -L: follow redirects; -m: total timeout
        curl_result=$(curl -L -m "$CURL_TIMEOUT_SEC" -s -o /dev/null -w "%{speed_download}" "$curl_url" 2>/dev/null)

        # 检查 curl 是否返回有效数字
        if [[ "$curl_result" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$curl_result > 0" | bc -l) )); then
            speed_kbps=$(awk "BEGIN {printf \"%.1f\", $curl_result / 1024}")
            # 判定网速状态
            if (( $(echo "$speed_kbps >= $SPEED_EXCELLENT" | bc -l) )); then
                speed_status="${GREEN}� 优秀${NC}"
            elif (( $(echo "$speed_kbps >= $SPEED_GOOD" | bc -l) )); then
                speed_status="${GREEN}� 良好${NC}"
            elif (( $(echo "$speed_kbps >= $SPEED_FAIR" | bc -l) )); then
                speed_status="${YELLOW}� 一般${NC}"
            elif (( $(echo "$speed_kbps >= $SPEED_POOR" | bc -l) )); then
                speed_status="${RED}� 较差${NC}"
            else
                speed_status="${RED}� 极差${NC}"
            fi
        else
            # curl 失败或速率为 0(如超时、空响应)
            speed_kbps="N/A"
            speed_status="${YELLOW}⚠️  无数据${NC}"
        fi

        # 构建结果字符串(含延迟 + 网速)
        if [ "$is_success" -eq 1 ]; then
            result="${site}(${desc}): ${avg_formatted} ms | ${speed_kbps} KB/s ($speed_status)"
        else
            result="${site}(${desc}): 成功但无延迟数据 | ${speed_kbps} KB/s ($speed_status)"
        fi
        echo "1|$avg|$speed_kbps|$result"
        return
    else
        # Ping 完全失败,不进行网速测试
        result="${site}(${desc}): ${RED}超时${NC} | --- KB/s (---)"
        echo "0|0|0|$result"
        return
    fi
}

# ┌──────────────────────────────┐
# │           主程序逻辑            │
# └──────────────────────────────┘

check_dependencies

clear
echo -e "${CYAN}� 实时网络质量监控(${OSTYPE})| 每 ${REFRESH_INTERVAL} 秒刷新 | Ctrl+C 退出 ${NC}"
echo -e "${BLUE}Ping: ${PING_COUNT} 次/站点 | Curl 网速测试超时: ${CURL_TIMEOUT_SEC}s ${NC}\n"

# 主循环:修复首次刷新延迟问题
first_run=true
while true; do
    start_time=$(date +%s.%N)

    total_avg=0
    total_speed=0
    valid_count=0
    speed_valid_count=0
    failed_sites=()
    site_results=()

    for entry in "${SITES[@]}"; do
        test_result=$(test_site "$entry")
        IFS='|' read -r is_success avg speed_kbps result <<< "$test_result"

        if [ "$is_success" -eq 1 ]; then
            total_avg=$(echo "$total_avg + $avg" | bc -l)
            ((valid_count++))

            # 累加有效网速(用于计算平均网速)
            if [[ "$speed_kbps" =~ ^[0-9.]+$ ]]; then
                total_speed=$(echo "$total_speed + $speed_kbps" | bc -l)
                ((speed_valid_count++))
            fi
        else
            IFS='|' read -r site desc <<< "$entry"
            failed_sites+=("$site")
        fi
        site_results+=("$result")
    done

    # 计算整体平均延迟
    if [ "$valid_count" -gt 0 ]; then
        overall_avg=$(echo "scale=2; $total_avg / $valid_count" | bc -l)
        avg_display=$(printf "%.1f" "$overall_avg")
    else
        overall_avg=9999
        avg_display="N/A"
    fi

    # 判定网络延迟状态(原"网络状态")
    if [ "$valid_count" -eq 0 ]; then
        delay_status="${RED}❌ 网络中断${NC}"
    elif (( $(echo "$overall_avg <= $EXCELLENT_THRESHOLD" | bc -l) )); then
        delay_status="${GREEN}� 优秀${NC}"
    elif (( $(echo "$overall_avg <= $GOOD_THRESHOLD" | bc -l) )); then
        delay_status="${GREEN}� 良好${NC}"
    elif (( $(echo "$overall_avg <= $FAIR_THRESHOLD" | bc -l) )); then
        delay_status="${YELLOW}� 一般${NC}"
    elif (( $(echo "$overall_avg <= $POOR_THRESHOLD" | bc -l) )); then
        delay_status="${RED}� 较差${NC}"
    else
        delay_status="${RED}� 极差${NC}"
    fi

    # 计算平均网速(仅基于有效测速)
    if [ "$speed_valid_count" -gt 0 ]; then
        avg_speed=$(echo "scale=2; $total_speed / $speed_valid_count" | bc -l)
        speed_display=$(printf "%.1f" "$avg_speed")
    else
        speed_display="N/A"
    fi

    # 判定整体网速状态(基于平均网速)
    if [ "$speed_valid_count" -eq 0 ]; then
        speed_status="${YELLOW}⚠️  无有效测速${NC}"
    elif (( $(echo "$avg_speed >= $SPEED_EXCELLENT" | bc -l) )); then
        speed_status="${GREEN}� 优秀${NC}"
    elif (( $(echo "$avg_speed >= $SPEED_GOOD" | bc -l) )); then
        speed_status="${GREEN}� 良好${NC}"
    elif (( $(echo "$avg_speed >= $SPEED_FAIR" | bc -l) )); then
        speed_status="${YELLOW}� 一般${NC}"
    elif (( $(echo "$avg_speed >= $SPEED_POOR" | bc -l) )); then
        speed_status="${RED}� 较差${NC}"
    else
        speed_status="${RED}� 极差${NC}"
    fi

    # === 原地刷新输出 ===
    clear
    echo -e "${CYAN}� 实时网络质量监控(${OSTYPE})| 每 ${REFRESH_INTERVAL} 秒刷新 | Ctrl+C 退出 ${NC}"
    echo -e "${BLUE}Ping: ${PING_COUNT} 次/站点 | Curl 网速测试超时: ${CURL_TIMEOUT_SEC}s ${NC}\n"

    # 输出核心统计
    echo "� 整体平均延迟: ${CYAN}${avg_display} ms${NC}   � 网络延迟状态: $delay_status"
    echo "� 平均下载速率: ${CYAN}${speed_display} KB/s${NC}   � 网速状态: $speed_status"
    echo "✅ 有效站点: $valid_count/${#SITES[@]}   ❌ 失败站点: ${#failed_sites[@]}"
    echo
    echo "${BLUE}各站点详情(延迟 | 网速): ${NC}"
    for res in "${site_results[@]}"; do
        echo "  • $res"
    done

    if [ ${#failed_sites[@]} -gt 0 ]; then
        echo
        echo "${YELLOW}⚠️  失败站点列表: ${NC} ${failed_sites[*]}"
    fi

    # === 修复关键:首次运行不 sleep,后续才 sleep ===
    if [ "$first_run" = true ]; then
        first_run=false
    else
        elapsed=$(echo "$(date +%s.%N) - $start_time" | bc)
        sleep_time=$(echo "$REFRESH_INTERVAL - $elapsed" | bc)
        if (( $(echo "$sleep_time > 0" | bc -l) )); then
            sleep "$sleep_time"
        fi
    fi
done

部署与运行指南

环境准备

确保系统已安装 bash, ping, curl, bc

  • macOS : bc 通常未预装,需执行:

    bash 复制代码
    brew install bc
  • CentOS/RHEL :

    bash 复制代码
    sudo yum install bc -y
  • Ubuntu/Debian :

    bash 复制代码
    sudo apt install bc -y
脚本配置

编辑脚本中的 可配置区域 以适配监控需求:

  • SITES : 修改监控域名列表,格式为 "域名|中文备注",支持注释掉不需要的站点。
  • REFRESH_INTERVAL: 调整刷新频率,建议 5-10 秒,过短会因网络耗时导致刷新延迟。
  • 阈值调整 : 根据实际网络环境(内网、专线、弱网)调整 EXCELLENT_THRESHOLD 等变量。
执行方式

赋予执行权限并运行:

bash 复制代码
chmod +x network-monitor.sh
./network-monitor.sh
退出监控

在终端按下 Ctrl + C 即可安全停止脚本。

输出界面示例

脚本运行后,终端将呈现如下动态看板(颜色在实际终端中显示):

text 复制代码
🌐 实时网络质量监控(linux-gnu)| 每 5 秒刷新 | Ctrl+C 退出 
Ping: 1 次/站点 | Curl 网速测试超时: 3s

📊 整体平均延迟: 45.2 ms   🟢 网络延迟状态: ✅ 优秀
🚀 平均下载速率: 185.4 KB/s   🟢 网速状态: ✅ 良好
✅ 有效站点: 7/7   ❌ 失败站点: 0

各站点详情(延迟 | 网速): 
  • www.baidu.com(百度首页): 32.5 ms | 83.8 KB/s (🟡 一般)
  • www.tencent.com(腾讯首页): 28.1 ms | 382.6 KB/s (🟢 优秀)
  • www.taobao.com(淘宝首页): 41.0 ms | 128.7 KB/s (🟡 一般)
  • www.douyin.com(抖音首页): 55.3 ms | 10.5 KB/s (🔴 极差)
  • doubao.com(豆包AI): 48.2 ms | 199.4 KB/s (🟢 良好)
  • qianwen.com(千问AI): 39.5 ms | 75.7 KB/s (🟡 一般)
  • yuanbao.tencent.com(元宝AI): 62.1 ms | 43.7 KB/s (🔴 较差)

注:若出现站点完全不可达,底部会额外显示黄色警告行:
⚠️ 失败站点列表:192.0.2.1

技术实现细节分析

跨平台 Ping 超时处理

Linux 的 ping -W 单位为秒(支持小数),macOS 的 ping -W 单位为毫秒。脚本通过检测 $OSTYPE 动态转换:

bash 复制代码
if [[ "$OSTYPE" == "darwin"* ]]; then
    # macOS: 秒转毫秒
    PING_TIMEOUT_MS=$(echo "scale=0; $PING_TIMEOUT_SEC * 1000" | bc -l)
    PING_W_PARAM="-W $PING_TIMEOUT_MS"
else
    # Linux: 直接使用秒
    PING_W_PARAM="-W $PING_TIMEOUT_SEC"
fi
数据解析鲁棒性

针对不同操作系统 ping 输出格式差异(Linux 显示 rtt min/avg/max/mdev,macOS 显示 round-trip min/avg/max/stddev),脚本采用通用正则匹配策略:

  • Linux: grep -E "(min/avg/max|rtt)"
  • macOS: grep "round-trip"
    提取 数字/数字/数字 格式字符串后,利用 IFS='/' 分割获取平均值。
浮点数运算

Bash 原生不支持浮点运算,脚本全程依赖 bc 工具进行精度控制:

  • 丢包率计算:scale=0; $PING_COUNT * (100 - $loss_percent) / 100
  • 速度单位换算:awk "BEGIN {printf \"%.1f\", $curl_result / 1024}"(混用 awk 格式化,bc 做逻辑判断)。
  • 阈值比较:(( $(echo "$speed_kbps >= $SPEED_EXCELLENT" | bc -l) ))
原子化输出与刷新

为避免输出乱码,脚本先将所有结果收集到数组 site_results 和变量中,计算完整体统计值后,执行一次 clear 清屏,再按顺序打印头部、统计摘要、详细列表和警告信息。这种"全量重绘"方式保证了界面整洁和数据同步性。

相关推荐
REDcker3 小时前
libevent、libev 与 libuv:对比、演进与实现原理
linux·c++·后端·编程·c·高并发·服务端
123过去3 小时前
impacket-mssqlclient使用教程
linux·测试工具·安全
Chase_______3 小时前
【Linux精讲|第1章】Vi 编辑器核心三模式——命令、插入、尾行全解析
linux·运维·编辑器
大虾别跑4 小时前
麒麟v10搭建rsync
linux·运维·服务器
桌面运维家4 小时前
Nginx+Keepalived:Linux高可用负载均衡配置实战
linux·nginx·负载均衡
BullSmall4 小时前
LVS与HAProxy高可用负载方案详解
linux·服务器·网络
docsz4 小时前
Flink-1.20集群部署
linux·服务器·flink
坤坤藤椒牛肉面4 小时前
常用知识点
linux
行思理4 小时前
Linux查看网站访问IP的命令大全
linux·服务器·前端