ℹ️ 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

参考

https://blog.csdn.net/qq_36981760/article/details/115713179