一条脚本搞定「端口探活 + 配置热更新 + 服务保活」,彻底告别手动重启与爆炸日志。
一、痛点:静态配置的尴尬
-
本地服务没启动,frpc 仍疯狂重试,日志秒级刷屏;
-
新增/下线服务要手动改 TOML → 重启,极易遗忘;
-
服务异常退出无人知晓,穿透直接失联。
二、思路:把配置做成「活」的
-
先探测:用 nc 检查 TCP/UDP 端口是否真实可达;
-
再合并:通过 md5 比对,仅当代理列表变化时才触发重启;
-
后保活:脚本末尾顺手
systemctl start frpc
,崩溃即自愈。
三、目录结构(一键即用)
/opt/frp
├── frpc # 官方二进制
├── frpc.base.toml # 全局配置(服务器地址、token 等)
├── frpc_proxies.json # 代理列表(只维护这一份)
├── frpc.service # frpc 服务文件,copy 到 /etc/systemd/system 目录后可选择删除
├── frpc-sync.sh # 核心脚本(赋予可执行权限)
└── frpc.toml # 脚本自动生成,勿手动编辑
运行后额外生成:
-
frpc-sync.log
– 脚本自身日志 -
frpc.log
– frpc 标准输出
四、核心脚本:frpc-sync.sh
#!/bin/bash
# 依赖:jq / nc / md5sum
set -eucd "$(dirname "$0")"BASE="./frpc.base.toml"
JSON="./frpc_proxies.json"
OUT="./frpc.toml.new" # 先写成临时文件,后面再决定是否覆盖正式配置
BACK="./frpc.toml.bak"
CUR="./frpc.toml"# ---------- 基础检查 ----------
for f in "$BASE" "$JSON"; do[[ -f $f ]] || { echo "❌ 找不到 $f" >&2; exit 1; }
done# ---------- 1. 生成新配置 ----------
cat "$BASE" > "$OUT"
echo >> "$OUT" # 两个空行
echo >> "$OUT"jq -c '.[]' "$JSON" | while IFS= read -r line; doname=$( echo "$line" | jq -r '.name')type=$( echo "$line" | jq -r '.type')localIP=$( echo "$line" | jq -r '.localIP')localPort=$(echo "$line" | jq -r '.localPort')# 类型合法性[[ $type == "tcp" || $type == "udp" ]] || { echo "Unsupported type: $type" >&2; exit 1; }# 探测if [[ $type == "tcp" ]]; thennc -z -w2 "$localIP" "$localPort" 2>/dev/null && ok=1 || ok=0elseok=$(echo -n "" | nc -u -w2 -v "$localIP" "$localPort" 2>&1 | grep -cE 'succeeded|open' || true)fiif [[ $ok -eq 1 ]]; thencat <<EOF >> "$OUT"
[[proxies]]
name = "$name"
type = "$type"
localIP = "$localIP"
localPort = $localPort
remotePort = $(echo "$line" | jq -r '.remotePort')EOFecho "[OK] $name ($type) ${localIP}:${localPort}"elseecho "[FAIL]$name ($type) ${localIP}:${localPort}"fi
done# ---------- 2. 一致性处理 ----------
# 清理旧临时文件
rm -f "$BACK"# 备份当前正式配置(若存在)
[[ -f $CUR ]] && cp "$CUR" "$BACK"# MD5 比对
if ! md5sum -c --quiet <(md5sum "$OUT") <(md5sum "$CUR" 2>/dev/null); thenmv "$OUT" "$CUR"echo "[$(date +"%F %T")] frpc.toml 配置已变更,重启 frpc 服务 ..."systemctl restart frpc
elseecho "[$(date +"%F %T")] frpc.toml 配置无变化,跳过重启。"rm -f "$OUT"
fi
rm -f "$BACK"# ---------- 3. 保活检查 ----------
if ! systemctl is-active --quiet frpc; thenecho "[$(date +"%F %T")] frpc 未运行,立即拉起 ..."systemctl start frpc
fi
给脚本加执行权限:
chmod +x /opt/frp/frpc-sync.sh
五、基础配置 frpc.base.toml(示例)
# 服务端配置
serverAddr = "x.x.x.x"
serverPort = 7000
auth.token = "xxxxxx"# 客户端web控制台配置
webServer.addr = "127.0.0.1"
webServer.port = 7400
webServer.user = "xxxxxx"
webServer.password = "xxxxxx"
六、代理列表 frpc_proxies.json(示例)
[{"name": "ubuntu-frpc-web","localIP": "127.0.0.1","localPort": 7400,"remotePort": 7400,"type": "tcp"},{"name": "ubuntu-ssh","localIP": "127.0.0.1","localPort": 22,"remotePort": 30022,"type": "tcp"},{"name": "ubuntu-3389","localIP": "127.0.0.1","localPort": 3389,"remotePort": 30389,"type": "tcp"}
]
七、systemd 服务
[Unit]
Description = frp client
# 关键:确保网络就绪后再启动(比 network.target 更严格,避免网络未通导致启动失败)
After = network-online.target syslog.target
Wants = network-online.target # 依赖网络完全就绪[Service]
Type = simple
# 启动frps的命令,需修改为您的frps的安装路径
ExecStart = /opt/frp/frpc -c /opt/frp/frpc.toml# 新增:将标准输出和错误输出重定向到日志文件
StandardOutput = append:/opt/frp/frpc.log
StandardError = append:/opt/frp/frpc.log# 可选:若启动失败,自动重启(增加容错)
Restart = on-failure
RestartSec = 5[Install]
# 关键:自启动目标必须正确(Ubuntu 默认多用户目标)
WantedBy = multi-user.target
八、systemd 一条龙操作(安装·启动·状态)
# 1. 安装服务文件
sudo cp /opt/frp/frpc.service /etc/systemd/system/# 2. 重新加载 systemd 配置
sudo systemctl daemon-reload# 3. 设置开机自启并立即启动
sudo systemctl enable --now frpc# 4. 查看实时状态
systemctl status frpc# 5. 实时日志(Ctrl+C 退出)
journalctl -u frpc -f# 6. 手动重启(配置变更后)
sudo systemctl restart frpc# 7. 停止与禁用(如需卸载)
sudo systemctl stop frpc
sudo systemctl disable frpc
九、日常用法
-
安装依赖
apt update && apt install -y jq netcat-openbsd
-
一次性 systemd 启动/自启
systemctl daemon-reload systemctl enable --now frpc
-
定时任务(每分钟检查一次)
crontab -e # 追加 * * * * * /opt/frp/frpc-sync.sh >> /opt/frp/frpc-sync.log 2>&1
-
观察日志
tail -f /opt/frp/frpc-sync.log # 脚本侧 tail -f /opt/frp/frpc.log # frpc 侧
十、回滚与调试
-
配置出错?
cp frpc.toml.bak frpc.toml && systemctl restart frpc
-
想临时关闭某个代理,直接删除
frpc_proxies.json
对应段,一分钟内自动生效。
十一、小结
能力 | 实现方式 |
---|---|
端口探活 | nc -z / nc -u |
动态配置 | jq 解析 JSON → 模板合并 |
无扰重启 | md5sum 比对 |
崩溃自愈 | 脚本末尾 systemctl start frpc |
日志集中 | 你已有的 frpc.log + 脚本 frpc-sync.log |
一次编写,终身「躺平」。
把脚本丢进服务器,frpc 从此只代理真正在线的端口,彻底告别手动维护和刷屏日志。祝你穿透愉快!
把脚本丢进服务器,frpc 从此只代理真正在线的端口,彻底告别手动维护和刷屏日志。祝你穿透愉快!