ℹ️ Info
此篇文章为
FAST,实现 Fast Reverse Proxy (FRP) 的理解与运行。另外这里演示用的是 Linux 系统,但为 Windows 也准备了运行脚本。
准备内容
下载 FRP
找到相应架构的文件 https://github.com/fatedier/frp
解压
tar -txvf 文件名
- 解压得如下内容:
frp_版本号_系统架构/
├── frps # 服务端核心程序 (Server)
├── frps.toml # 服务端配置文件
├── frpc # 客户端核心程序 (Client)
├── frpc.toml # 客户端配置文件
└── LICENSE # 开源协议
文件权限更改
- 给服务端执行权限
chmod +x frps
- 给客户端执行权限
chmod +x frpc
正式操作
配置文件修改
-
frps 配置文件修改
- 编辑
frps.toml
- 编辑
# frps 对外 TCP 端口
bindPort = 7000
# frps 对外 QUIC 协议的 UDP 端口
# 需在 frpc.toml 额外配置 transport.protocol = "quic"
quicBindPort = 7000
- 启动frps:
# 前台启动
./frps -c ./frps.toml
# 后台启动
./frps -c ./frps.toml &
- frpc 配置文件修改
- 编辑
frpc.toml
- 编辑
# 服务端运行端口
serverPort = 7000
[[proxies]]
name = "test-tcp"
type = "tcp"
localIP = "127.0.0.1" # 本地ip
localPort = 22 # 想转发的(本地)端口
remotePort = 6000 # 转发到(服务器的)此端口
文章总结
脚本
💡 Tip
为了方便使用,准备一个维护脚本可以提供极大便利。
Linux
只用修改配置区的两处即可使用了
#!/bin/bash
# ================= 配置区 =================
# frpc 可执行文件的绝对路径
FRPC="/root/frp/frpc"
# 配置文件所在的目录
CONFIG_DIR="/root/frp/config"
# ==========================================
# 定义终端颜色
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 检查 frpc 本体是否存在
if [ ! -x "$FRPC" ]; then
echo -e "${RED}错误: 找不到 frpc 可执行文件,请检查路径: $FRPC${NC}"
exit 1
fi
# 检查配置目录是否存在
if [ ! -d "$CONFIG_DIR" ]; then
echo -e "${RED}错误: 找不到配置目录: $CONFIG_DIR${NC}"
exit 1
fi
# 封装启动逻辑
start_instance() {
local conf="$1"
if [ ! -f "$conf" ]; then
echo -e "${YELLOW}警告: 配置文件不存在,已跳过: $conf${NC}"
return
fi
local conf_name=$(basename "$conf")
if pgrep -f "frpc -c .*${conf_name}" > /dev/null; then
echo -e "${YELLOW}实例已在运行,已跳过: $conf_name${NC}"
return
fi
$FRPC -c "$conf" > "${conf%.*}.log" 2>&1 &
echo -e "${GREEN}已成功启动: $conf_name${NC}"
}
# 封装停止逻辑
stop_instance() {
local pid="$1"
local conf="$2"
kill "$pid"
echo -e "${RED}已停止: PID $pid ($conf)${NC}"
}
# 核心执行逻辑(支持交互与命令行传参)
execute_action() {
local action="$1"
local input_choices="$2"
local items=()
local pids=()
# 动态获取配置文件列表
mapfile -t items < <(find "$CONFIG_DIR" -maxdepth 1 -type f \( -iname "*.toml" -o -iname "*.ini" \) | sort)
# 如果是 stop 或 list,先获取当前运行的进程
if [ "$action" == "stop" ] || [ "$action" == "list" ]; then
mapfile -t pids < <(pgrep -f "frpc -c")
fi
# 处理 list 命令
if [ "$action" == "list" ]; then
echo "===== 当前 frpc 实例状态 ====="
if [ ${#items[@]} -eq 0 ]; then
echo -e "${YELLOW}未找到任何配置文件。${NC}"
return
fi
for i in "${!items[@]}"; do
conf="${items[$i]}"
conf_name=$(basename "$conf")
if pgrep -f "frpc -c .*${conf_name}" > /dev/null; then
status="${GREEN}[运行中]${NC}"
else
status="${RED}[已停止]${NC}"
fi
printf "[%d] 配置文件: %-30s %b\n" "$((i+1))" "$conf_name" "$status"
done
return
fi
# 处理 start 或 stop 的列表展示
if [ "$action" == "start" ]; then
if [ ${#items[@]} -eq 0 ]; then
echo -e "${YELLOW}在 $CONFIG_DIR 目录下未找到任何 .toml 或 .ini 配置文件。${NC}"
return
fi
echo "===== 可用的 frpc 配置实例 ====="
for i in "${!items[@]}"; do
conf="${items[$i]}"
conf_name=$(basename "$conf")
if pgrep -f "frpc -c .*${conf_name}" > /dev/null; then
status="${GREEN}[运行中]${NC}"
else
status="${RED}[已停止]${NC}"
fi
printf "[%d] 配置文件: %-30s %b\n" "$((i+1))" "$conf_name" "$status"
done
else
if [ ${#pids[@]} -eq 0 ]; then
echo -e "${YELLOW}当前没有运行中的 frpc 实例。${NC}"
return
fi
echo "===== 正在运行的 frpc 实例 ====="
for i in "${!pids[@]}"; do
conf=$(ps -p "${pids[$i]}" -o args= | awk -F'-c ' '{print $2}' | awk '{print $1}')
printf "[%d] PID: %-8s 配置文件: %-30s ${GREEN}[运行中]${NC}\n" "$((i+1))" "${pids[$i]}" "$(basename "$conf")"
done
fi
# 如果没有传入参数,则进入交互式输入
if [ -z "$input_choices" ]; then
local max_choice
if [ "$action" == "start" ]; then
max_choice=${#items[@]}
else
max_choice=${#pids[@]}
fi
echo "[0] ${action^}所有实例"
echo "================================"
read -rp "请输入要 ${action} 的实例序号 (多个请用英文逗号隔开,如 1,3,5): " input_choices
fi
# 校验输入格式
if ! [[ "$input_choices" =~ ^[0-9,]+$ ]]; then
echo -e "${RED}输入格式无效,请仅使用数字和英文逗号。${NC}"
return
fi
IFS=',' read -r -a choices <<< "$input_choices"
local max_choice
if [ "$action" == "start" ]; then
max_choice=${#items[@]}
else
max_choice=${#pids[@]}
fi
for c in "${choices[@]}"; do
if [ "$c" -gt "$max_choice" ]; then
echo -e "${RED}序号 $c 超出范围,已跳过。${NC}"
continue
fi
if [ "$c" -eq 0 ]; then
if [ "$action" == "start" ]; then
for conf in "${items[@]}"; do start_instance "$conf"; done
else
for pid in "${pids[@]}"; do
conf=$(ps -p "$pid" -o args= | awk -F'-c ' '{print $2}' | awk '{print $1}')
stop_instance "$pid" "$conf"
done
fi
else
local index=$((c-1))
if [ "$action" == "start" ]; then
start_instance "${items[$index]}"
else
stop_instance "${pids[$index]}" "$(ps -p "${pids[$index]}" -o args= | awk -F'-c ' '{print $2}' | awk '{print $1}')"
fi
fi
done
}
# 主程序入口
case "$1" in
start) execute_action "start" "$2" ;;
stop) execute_action "stop" "$2" ;;
list) execute_action "list" ;;
*)
echo "用法: $0 {start|stop|list} [序号]"
echo "示例:"
echo " $0 start # 交互式启动"
echo " $0 start 1,3,5 # 批量启动 1,3,5 号实例"
echo " $0 stop 2 # 停止 2 号实例"
echo " $0 list # 查看所有实例状态"
;;
esac
Windows
⚠️ Warning
给 Windows 写脚本需要更改脚本的
编码格式和行尾序列否则无法运行或运行时出现乱码,另外 Windows 脚本我用的是相对路径需将
编码格式改为GBK,行尾序列改为CRLF
@echo off
setlocal EnableDelayedExpansion
:: ================= 配置区 =================
set "FRPC=%~dp0frpc.exe"
set "CONFIG_DIR=%~dp0config"
:: ==========================================
:: 【关键】强制锁定当前目录和盘符
pushd "%~dp0" >nul 2>&1
if not exist "%FRPC%" (
call :print_color "错误: 找不到 frpc.exe" "Red"
pause & exit /b 1
)
if not exist "%CONFIG_DIR%" (
call :print_color "错误: 找不到 config 文件夹" "Red"
pause & exit /b 1
)
:: ================= 模式判断 =================
if "%~1"=="" (
set "MODE=MENU"
goto :main_menu
)
set "MODE=CMD"
if "%~1"=="list" goto :do_list_with_pause
if "%~1"=="start" goto :handle_start_interactive
if "%~1"=="stop" goto :handle_stop_interactive
if "%~1"=="stopall" goto :handle_stop_all
call :print_color "=== FRPC 管理脚本 ===" "Cyan"
echo 用法: %~nx0 [命令]
echo list 列出所有配置及运行状态
echo start 启动配置 (支持 1,3,5 或 all)
echo stop 停止配置 (支持 1,3,5 或 all)
echo stopall 一键停止所有正在运行的配置
exit /b
:: ================= 主菜单功能 =================
:main_menu
cls
call :print_color "========== FRPC 管理控制台 ==========" "Cyan"
echo.
echo [1] 查看状态列表 (list)
echo [2] 启动配置 (start)
echo [3] 停止配置 (stop)
echo [4] 一键全部停止 (stopall)
echo [0] 退出脚本
echo.
choice /c 12340 /n /m "请输入数字选择操作: "
set "menu_choice=%errorlevel%"
if "%menu_choice%"=="1" goto :do_list_with_pause
if "%menu_choice%"=="2" goto :handle_start_interactive
if "%menu_choice%"=="3" goto :handle_stop_interactive
if "%menu_choice%"=="4" goto :handle_stop_all
if "%menu_choice%"=="5" exit /b
:: ================= 功能实现区 =================
:: 【纯列表显示】不带 pause,专门给启动/停止流程使用
:do_list
cls
echo.
echo ===== 可用的 frpc 配置实例 =====
set /a count=0
set "temp_list=%TEMP%\frp_ps_%RANDOM%.txt"
powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='frpc.exe'\" | Select-Object -ExpandProperty CommandLine" > "%temp_list%" 2>nul
for %%f in ("%CONFIG_DIR%\*.ini", "%CONFIG_DIR%\*.toml") do (
set /a count+=1
set "file_!count!=%%~nxf"
set "check_name=%%~nxf"
set "status=[已停止]"
set "color=Red"
findstr /I "!check_name!" "%temp_list%" >nul 2>&1
if !errorlevel! equ 0 (
set "status=[运行中]"
set "color=Green"
)
<nul set /p "= [!count!] 配置文件: !check_name! "
call :print_color "!status!" "!color!"
)
echo.
if %count% equ 0 call :print_color " (未找到任何配置文件)" "Yellow"
del /f /q "%temp_list%" >nul 2>&1
exit /b
:: 【带暂停的列表显示】专门给选 1 或命令行 list 使用
:do_list_with_pause
call :do_list
pause
exit /b
:: 2. 交互式启动
:handle_start_interactive
call :do_list
echo.
call :print_color "请输入要启动的序号(多个用逗号分隔)或 a(全部):" "Cyan"
set "choice="
<nul set /p "= >>> 输入: "
set /p choice=""
if /i "%choice%"=="a" set "choice=all"
call :batch_execute "start" "%choice%"
exit /b
:: 3. 交互式停止
:handle_stop_interactive
call :do_list
echo.
call :print_color "请输入要停止的序号(多个用逗号分隔)或 a(全部):" "Yellow"
set "choice="
<nul set /p "= >>> 输入: "
set /p choice=""
if /i "%choice%"=="a" set "choice=all"
call :batch_execute "stop" "%choice%"
exit /b
:: 4. 一键全部停止
:handle_stop_all
cls
call :print_color ">>> 正在停止所有运行中的 frpc 进程..." "Yellow"
powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='frpc.exe'\" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }" 2>nul
call :print_color " 已发送全部停止信号" "Green"
exit /b
:: ================= 核心批量执行逻辑 =================
:batch_execute
set "action=%~1"
set "input=%~2"
set "input=%input:,= %"
for %%i in (%input%) do (
set "idx=%%i"
if defined file_%%i (
call :do_single_action "!action!" !idx!
) else (
call :print_color " [跳过] 序号 %%i 无效" "Yellow"
)
)
exit /b
:do_single_action
set "act=%~1"
set "idx=%~2"
set "curr_file=!file_%idx%!"
if "!act!"=="start" (
set "temp_check=%TEMP%\frp_chk_%RANDOM%.txt"
powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='frpc.exe'\" | Select-Object -ExpandProperty CommandLine" > "!temp_check!" 2>nul
findstr /I "!curr_file!" "!temp_check!" >nul 2>&1
if !errorlevel! equ 0 (
call :print_color " [!curr_file!] 已在运行,跳过启动" "Yellow"
) else (
call :print_color ">>> 正在启动: !curr_file!" "Cyan"
powershell -NoProfile -Command "Start-Process '!FRPC!' -ArgumentList '-c', '!CONFIG_DIR!\!curr_file!' -WindowStyle Hidden" 2>nul
timeout /t 1 >nul
call :print_color " 启动成功 (后台静默)" "Green"
)
del /f /q "!temp_check!" >nul 2>&1
) else if "!act!"=="stop" (
call :print_color ">>> 正在停止: !curr_file!" "Yellow"
powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='frpc.exe'\" | Where-Object { $_.CommandLine -like '*!curr_file!*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }" 2>nul
call :print_color " 已发送停止信号" "Green"
)
exit /b
:: ================= 颜色输出函数 =================
:print_color
powershell -NoProfile -Command "Write-Host '%~1' -ForegroundColor '%~2' -NoNewline; Write-Host ''"
exit /b
popd >nul 2>&1