内网穿透进阶:让 frpc 只代理「真正在线」的端口

news/2025/10/15 0:17:27/文章来源:https://www.cnblogs.com/nihaorz/p/19142310
一条脚本搞定「端口探活 + 配置热更新 + 服务保活」,彻底告别手动重启与爆炸日志。

一、痛点:静态配置的尴尬

  • 本地服务没启动,frpc 仍疯狂重试,日志秒级刷屏;
  • 新增/下线服务要手动改 TOML → 重启,极易遗忘;
  • 服务异常退出无人知晓,穿透直接失联。

二、思路:把配置做成「活」的

  1. 先探测:用 nc 检查 TCP/UDP 端口是否真实可达;
  2. 再合并:通过 md5 比对,仅当代理列表变化时才触发重启;
  3. 后保活:脚本末尾顺手 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
 

九、日常用法

  1. 安装依赖
    apt update && apt install -y jq netcat-openbsd
     
  2. 一次性 systemd 启动/自启
    systemctl daemon-reload
    systemctl enable --now frpc
     
  3. 定时任务(每分钟检查一次)
    crontab -e
    # 追加
    * * * * * /opt/frp/frpc-sync.sh >> /opt/frp/frpc-sync.log 2>&1
     
  4. 观察日志
    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 从此只代理真正在线的端口,彻底告别手动维护和刷屏日志。祝你穿透愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/937184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

规则逻辑与人文逻辑的统一:AI元人文构想的演进之路

规则逻辑与人文逻辑的统一:AI元人文构想的演进之路 在人工智能发展的关键转折点,我们面临着深刻的认知跃迁:规则逻辑与人文逻辑并非对立的两极,而是智能进化道路上相互依存、彼此成就的必然维度。AI元人文构想以其…

2023 ICPC Jinan

2023 ICPC Jinan ICPC Jinan G 考虑找矛盾。首先对于同一行,翻转和不翻是一个矛盾,对于相异的行,若一行的翻转或不反转会使同一列产生多余的 1,则又是一个矛盾。将每一行拆成两个点,一个点代表不翻转该行,一个点…

二叉树中和为目标值的路径

LCR 153. 二叉树中和为目标值的路径 LCR 153. 二叉树中和为目标值的路径参考题解前言 该题考察二叉树中的回溯,使用先序遍历以及路径记录 先序遍历:根左右 路径记录:通过一个“中间人”(path)来记录当前的路径和,…

动态库的调用方式

在 Linux 中,动态库(.so文件)的调用方式主要有两种:编译时链接(隐式调用)和运行时加载(显式调用)。 一、编译时链接(隐式调用) 这种方式在编译阶段就指定动态库,程序启动时会自动加载依赖的动态库,适用于已…

动态库的调用方式

在 Linux 中,动态库(.so文件)的调用方式主要有两种:编译时链接(隐式调用)和运行时加载(显式调用)。 一、编译时链接(隐式调用) 这种方式在编译阶段就指定动态库,程序启动时会自动加载依赖的动态库,适用于已…

云原生技术概览

云原生技术概览书籍:https://jimmysong.io/kubernetes-handbook/从云计算到微服务再到云原生计算 下面将从云计算的发展历程引入云原生计算 云计算介绍 云计算演进历程云计算就是一种配置资源的方式,根据资源配置方式…

OAM角色定义

OAM角色定义https://github.com/oam-dev/spec/blob/master/introduction.md关注点分离 开放应用程序模型提出了开发人员负责的部分与平台工程师负责的部分之间明确的关注点分离。 Open Application Model proposed a c…

OCI

OCI【译者的话】本文介绍了OCI运行时和镜像规范,以及在过去的一段时间里大家对该规范的一些误解。OCI规范制订工作尚未落幕,未来也将对容器产生更加深远的影响。 【3 天烧脑式基于Docker的CI/CD实战训练营 | 北京站】…

消灭重复代码的最佳实践

消灭重复代码的最佳实践代码重复本身不可怕,可怕的是漏改或改错。消灭重复代码,降低改动可能引入的风险。学习笔记:https://time.geekbang.org/column/article/228964工厂模式 + 模板方法 消除 if else 和重复代码 …

Spring应用上下文的获取和保存Bean

Spring应用上下文的获取和保存BeanSpring 容器是 IOC 容器,但是,反过来却不成立不常用的容器实现 -- BeanFactory:最简单的容器,提供基本的 DI 功能 高级实现,继承 BeanFactory 派生的应用上下文 -- ApplicationC…

Redis的数据类型选择

Redis的数据类型选择String 几乎所有的数据都可以使用 String 来存储。浪费存储空间,key 也是需要存储空间的 管理、维护成为噩梦,Redis 中存在着大量的 KV 对象 key 冲突的几率变高(不同的业务系统共用一个 Redis …

pipeline解决Redis频繁命令往返导致的性能瓶颈

pipeline解决Redis频繁命令往返导致的性能瓶颈客户端和服务端交互模型Redis 的 pipeline 交互模型Redis pipeline 与原生批量mset等等命令对比原生批量命令是原子性,pipeline 是非原子性的 原生批量命令一次只能执行一…

SpringBean实例化之前做点事情

SpringBean实例化之前做点事情SpringBean 实例化 需求,实现一个视频解码器,可以实现各种视频格式的解码 第一种方法:先去实现各个解码器,然后 if.else 判断使用哪种解码器使用 BeanPostProcessor 优化 第二种方法:…

SpringBoot定时任务不定时执行了

SpringBoot定时任务不定时执行了两个注解 @EnableScheduling:使得@Scheduled生效 @Scheduled:标注在方法上 @Scheduled 注解 fixedDelay任务结束与开始之间的间隔 fixedRate两次任务的开始的间隔,不管任务是不是执行…

依赖冲突的发现和解决

依赖冲突的发现和解决依赖冲突的发现现象一:一个类的行为不按照预期,本来这个类应该是有这个方法的,但是引入一个新的依赖之后就开始报错找不到方法了,一般就是依赖冲突的时候 Maven 自己选择了一个不符合自己预期…

javaLong类型在前端json数据损失精度

javaLong类型在前端json数据损失精度方法一:重新生成pojo对象,将所有数据库类型为bigint都映射成String类 方法二:对于使用springboot,则增加配置代码: package com.gj.app.config;import com.fasterxml.jackson.…

校招面试官揭秘:我们到底在寻找什么样的技术人才?

🎯 校招面试官揭秘:我们到底在寻找什么样的技术人才?又是一年校招季!📚 作为技术团队的Leader,这段时间我面试了不少优秀的同学。有人拿到心仪offer,也有人遗憾错过机会...今天想从面试官的角度,和大家聊聊我…

时间格式不能正常转换?

时间格式不能正常转换?@DateTimeFormat 注解 前台到后台的时间格式转换,对于GET 请求参数可以正常工作。对于 POST 请求会报反序列化错误POST 请求会使用 JSON 反序列化,不会使用 @DateTimeFormat 注解。POST 表单(…

群发红包系统

群发红包系统业务流程 发红包输入金额以及人数 创建红包订单(订单ID,金额,份数) 调用支付系统 红包订单支付之后红包就发出去了 钱先拆好(行锁分散,加大并发)抢红包抢红包业务群,检测当前是否有剩余钱 没有剩余…

day011

今日完成:mysql的初步认识以及cmd指令操作 明日完成:mysql 遇到问题:无