最新的openwrt (04.21.2026)如果使用编译添加了frpc,启动会报错:
bash
frp服务启动失败,请检查服务端 "TCP多路复用(tcp_mux)"设置,确保与客户端完全一致!
不要被这个坑爹的提示误导了。它的实际意义是:
frpc进程没有在运行,而已!
究其原因,是因为启动脚本一个字没变,但是最新的服务启动停止命令变成start-stop-daemon了!
于是乎启动服务脚本失效了!
解决办法,就是修改/etc/init.d/frp这个服务,修改为以下内容:
bash
#!/bin/sh /etc/rc.common
START=99
LOGFILE="/var/etc/frp/frpc.log"
echo_date(){
local log=$1
echo $(date +%Y/%m/%d\ %X): "$log" >> $LOGFILE
}
Reduce_Log(){
local log=$1
[ ! -f "$log" ] && return
local sc=200
[ -n "$2" ] && sc=$2
local count=$(grep -c "" $log)
if [ $count -gt $sc ];then
let count=count-$sc
sed -i "1,$count d" $log
fi
}
conf_proxy_add() {
local cfg="$1"
local tmpconf="$2"
local enable type domain_type custom_domains remote_port local_ip local_port enable_http_auth enable_host_header_rewrite host_header_rewrite
local subdomain proxy_protocol_version use_encryption use_compression http_user http_pwd remark locations
local enable_plugin plugin plugin_http_user plugin_http_passwd plugin_unix_path stcp_role stcp_secretkey stcp_servername xtcp_role xtcp_secretkey xtcp_servername
local enable_https_plugin https_plugin plugin_local_addr plugin_crt_path plugin_key_path plugin_host_header_rewrite plugin_header_X_From_Where
config_get_bool enable "$cfg" enable 1
[ "$enable" -gt 0 ] || return 1
config_get type "$cfg" type
config_get custom_domains "$cfg" custom_domains
config_get subdomain "$cfg" subdomain
config_get remote_port "$cfg" remote_port
config_get local_ip "$cfg" local_ip
config_get local_port "$cfg" local_port
config_get locations "$cfg" locations
config_get host_header_rewrite "$cfg" host_header_rewrite
config_get http_user "$cfg" http_user
config_get http_pwd "$cfg" http_pwd
config_get remark "$cfg" remark
config_get plugin "$cfg" plugin
config_get plugin_http_user "$cfg" plugin_http_user
config_get plugin_http_passwd "$cfg" plugin_http_passwd
config_get plugin_unix_path "$cfg" plugin_unix_path
config_get stcp_role "$cfg" stcp_role
config_get stcp_secretkey "$cfg" stcp_secretkey
config_get stcp_servername "$cfg" stcp_servername
config_get xtcp_role "$cfg" xtcp_role
config_get xtcp_secretkey "$cfg" xtcp_secretkey
config_get xtcp_servername "$cfg" xtcp_servername
config_get proxy_protocol_version "$cfg" proxy_protocol_version
config_get https_plugin "$cfg" https_plugin
config_get plugin_local_addr "$cfg" plugin_local_addr
config_get plugin_crt_path "$cfg" plugin_crt_path
config_get plugin_key_path "$cfg" plugin_key_path
config_get plugin_host_header_rewrite "$cfg" plugin_host_header_rewrite
config_get plugin_header_X_From_Where "$cfg" plugin_header_X_From_Where
[ -n "$remark" ] && [ -n "$type" ] || return 1
echo "" >>$tmpconf
echo "[$remark]" >>$tmpconf
echo "type=$type" >>$tmpconf
[ -n "$custom_domains" ] && echo "custom_domains=$custom_domains" >>$tmpconf
[ -n "$subdomain" ] && echo "subdomain=$subdomain" >>$tmpconf
[ -n "$remote_port" ] && echo "remote_port=$remote_port" >>$tmpconf
[ -z "$stcp_role" ] && [ -z "$xtcp_role" ] && [ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
[ -z "$stcp_role" ] && [ -z "$xtcp_role" ] && [ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
[ -n "$locations" ] && echo "locations=$locations" >>$tmpconf
[ -n "$http_user" -a -n "$http_pwd" ] && {
echo "http_user=$http_user" >>$tmpconf
echo "http_pwd=$http_pwd" >>$tmpconf
}
[ -n "$host_header_rewrite" ] && echo "host_header_rewrite=$host_header_rewrite" >>$tmpconf
[ -n "$plugin" ] && echo "plugin=$plugin" >>$tmpconf
[ -n "$plugin_http_user" -a -n "$plugin_http_passwd" ] && {
echo "plugin_http_user=$plugin_http_user" >>$tmpconf
echo "plugin_http_passwd=$plugin_http_passwd" >>$tmpconf
}
[ -n "$plugin_unix_path" ] && echo "plugin_unix_path=$plugin_unix_path" >>$tmpconf
[ -n "$stcp_role" ] && {
if [ "$stcp_role" == "visitor" ]; then
echo "role=$stcp_role" >>$tmpconf
[ -n "$local_ip" ] && echo "bind_addr=$local_ip" >>$tmpconf
[ -n "$local_port" ] && echo "bind_port=$local_port" >>$tmpconf
[ -n "$stcp_servername" ] && echo "server_name=$stcp_servername" >>$tmpconf || return 1
else
[ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
[ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
fi
[ -n "$stcp_secretkey" ] && echo "sk=$stcp_secretkey" >>$tmpconf || return 1
}
[ -n "$xtcp_role" ] && {
if [ "$xtcp_role" == "visitor" ]; then
echo "role=$xtcp_role" >>$tmpconf
[ -n "$local_ip" ] && echo "bind_addr=$local_ip" >>$tmpconf
[ -n "$local_port" ] && echo "bind_port=$local_port" >>$tmpconf
[ -n "$xtcp_servername" ] && echo "server_name=$xtcp_servername" >>$tmpconf || return 1
else
[ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
[ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
fi
[ -n "$xtcp_secretkey" ] && echo "sk=$xtcp_secretkey" >>$tmpconf || return 1
}
[ -n "$proxy_protocol_version" ] && {
if [ "$proxy_protocol_version" != "disable" ]; then
echo "proxy_protocol_version=$proxy_protocol_version" >>$tmpconf
fi
}
[ -n "$https_plugin" ] && echo "plugin=$https_plugin" >>$tmpconf
[ -n "$plugin_local_addr" ] && echo "plugin_local_addr=$plugin_local_addr" >>$tmpconf
[ -n "$plugin_crt_path" -a -n "$plugin_key_path" ] && {
echo "plugin_crt_path=$plugin_crt_path" >>$tmpconf
echo "plugin_key_path=$plugin_key_path" >>$tmpconf
}
[ -n "$plugin_host_header_rewrite" ] && echo "plugin_host_header_rewrite=$plugin_host_header_rewrite" >>$tmpconf
[ -n "$plugin_header_X_From_Where" ] && echo "plugin_header_X_From_Where=$plugin_header_X_From_Where" >>$tmpconf
frp_write_bool use_encryption $cfg 1
frp_write_bool use_compression $cfg 1
}
frp_write_bool() {
local opt="$1"
local config="$2"
local def="$3"
local val
config_get_bool val $config "$opt" "$def"
if [ "$val" -eq 0 ]; then
echo "${opt}=false" >> $tmpconf
else
echo "${opt}=true" >> $tmpconf
fi
}
frp_add_cru(){
time=$1
if [ ! -f "/etc/crontabs/root" ] || [ -z "$(cat /etc/crontabs/root | grep frp)" ]; then
sed -i '/frp/d' /etc/crontabs/root >/dev/null 2>&1
echo "*/$time * * * * /etc/init.d/frp restart" >> /etc/crontabs/root
fi
}
frp_del_cru(){
if [ ! -f "/etc/crontabs/root" ] || [ -n "$(cat /etc/crontabs/root | grep frp)" ]; then
sed -i '/frp/d' /etc/crontabs/root >/dev/null 2>&1
fi
}
download_binary(){
echo_date "开始下载frpc二进制文件..."
/usr/bin/wget --no-check-certificate --timeout=10 --tries=1 -o $LOGFILE https://github.com/fatedier/frp/releases/download/v0.13.0/frp_0.13.0_linux_arm.tar.gz -O /tmp/frp_0.13.0_linux_arm.tar.gz
[ ! -s "/tmp/frp_0.13.0_linux_arm.tar.gz" ] && /usr/bin/wget -q --no-check-certificate --timeout=10 --tries=1 https://any.mokoo.xyz/app/frp_0.13.0_linux_arm.tar.gz -O /tmp/frp_0.13.0_linux_arm.tar.gz
[ -f "/tmp/frp_0.13.0_linux_arm.tar.gz" ] && tar -xf /tmp/frp_0.13.0_linux_arm.tar.gz -C /tmp && \
mv /tmp/frp_0.13.0_linux_arm/frpc /usr/bin/frpc
rm -rf /tmp/frp_0.13.0_linux_arm*
if [ -f "/usr/bin/frpc" ]; then
chmod +x /usr/bin/frpc && echo_date "成功下载frpc二进制文件"
else
echo_date "下载frpc二进制文件失败,请重试!"
fi
}
boot() {
sleep 10s
start
}
start() {
start_service
}
stop() {
stop_service
}
start_service() {
config_load "frp"
local enabled server_addr server_port time privilege_token user tcp_mux enable_cpool tls_enable
local pool_count log_level log_max_days login_fail_exit http_proxy protocol admin_port admin_user admin_pwd
local tls_cert_file tls_key_file tls_trusted_ca_file
config_get_bool enabled common enabled 1
[ "$enabled" -gt 0 ] || return 1
config_get server_addr common server_addr
config_get server_port common server_port
config_get token common token
config_get user common user
config_get enable_cpool common enable_cpool
config_get pool_count common pool_count
config_get log_level common log_level
config_get log_max_days common log_max_days
config_get http_proxy common http_proxy
config_get protocol common protocol
config_get time common time
config_get admin_port common admin_port
config_get admin_user common admin_user
config_get admin_pwd common admin_pwd
config_get tls_cert_file common tls_cert_file
config_get tls_key_file common tls_key_file
config_get tls_trusted_ca_file common tls_trusted_ca_file
mkdir -p /var/etc/frp
[ ! -f "$LOGFILE" ] && touch $LOGFILE
[ ! -f "/usr/bin/frpc" ] && download_binary
[ ! -f "/usr/bin/frpc" ] && logger -t Frp 'Download frpc failed, please retry.' && exit 0
local tmpconf="/var/etc/frp/frpc.conf"
echo "[common]" >$tmpconf
echo "server_addr=${server_addr}" >>$tmpconf
echo "server_port=${server_port}" >>$tmpconf
echo "token=${token}" >>$tmpconf
[ -n "$user" ] && echo "user=$user" >>$tmpconf
echo "log_level=${log_level}" >>$tmpconf
echo "log_max_days=${log_max_days}" >>$tmpconf
echo "protocol=${protocol}" >>$tmpconf
echo "log_file=$LOGFILE" >>$tmpconf
[ -n "$http_proxy" ] && echo "http_proxy=$http_proxy" >>$tmpconf
[ -n "$pool_count" ] && echo "pool_count=$pool_count" >>$tmpconf
[ -n "$admin_port" ] && echo "admin_addr=0.0.0.0" >>$tmpconf && echo "admin_port=$admin_port" >>$tmpconf
[ -n "$admin_user" ] && echo "admin_user=$admin_user" >>$tmpconf
[ -n "$admin_pwd" ] && echo "admin_pwd=$admin_pwd" >>$tmpconf
[[ -n "$tls_cert_file" && -n "$tls_key_file" ]] && echo "tls_cert_file=$tls_cert_file" >>$tmpconf && echo "tls_key_file=$tls_key_file" >>$tmpconf
[ -n "$tls_trusted_ca_file" ] && echo "tls_trusted_ca_file=$tls_trusted_ca_file" >>$tmpconf
config_load "frp"
frp_write_bool tcp_mux common 1
frp_write_bool tls_enable common 0
frp_write_bool login_fail_exit common 1
config_foreach conf_proxy_add proxy "$tmpconf"
[ "$(cat "$tmpconf" | grep -c "type=")" -gt 0 ] || (echo_date "frp服务启动失败,请首先添加服务列表!" && return 1)
start-stop-daemon -S -b -m -p /var/run/frpc.pid -x /usr/bin/frpc -- -c "$tmpconf"
[ "$time" -gt 0 ] && frp_add_cru $time
# 延迟检查进程是否启动成功
sleep 2
[ -z "$(pgrep /usr/bin/frpc)" ] && echo_date 'frp服务启动失败,请检查服务端 "TCP多路复用(tcp_mux)"设置,确保与客户端完全一致!'
}
stop_service() {
frp_del_cru
if [ -n "$(pidof frpc)" ]; then
logger -t FRPC 'Shutting down frp service'
# 优雅停止
kill $(pidof frpc) 2>/dev/null
# 最多等5秒
for i in $(seq 1 5); do
sleep 1
pidof frpc >/dev/null || break
done
# 兜底强杀
kill -9 $(pidof frpc) 2>/dev/null
fi
Reduce_Log $LOGFILE
}
或者用procd的模式:
bash
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
LOGFILE="/var/etc/frp/frpc.log"
# ================= 工具函数 =================
echo_date(){
echo "$(date +%Y/%m/%d\ %X): $1" >> $LOGFILE
}
Reduce_Log(){
local log=$1
[ ! -f "$log" ] && return
local sc=${2:-200}
local count=$(wc -l < "$log")
if [ "$count" -gt "$sc" ]; then
sed -i "1,$((count-sc))d" "$log"
fi
}
# ================= frp辅助函数 =================
frp_write_bool() {
local opt="$1"
local config="$2"
local def="$3"
local val
config_get_bool val $config "$opt" "$def"
if [ "$val" -eq 0 ]; then
echo "${opt}=false" >> $tmpconf
else
echo "${opt}=true" >> $tmpconf
fi
}
conf_proxy_add() {
local cfg="$1"
local tmpconf="$2"
local enable type remark local_ip local_port remote_port custom_domains subdomain
config_get_bool enable "$cfg" enable 1
[ "$enable" -gt 0 ] || return
config_get type "$cfg" type
config_get remark "$cfg" remark
config_get local_ip "$cfg" local_ip
config_get local_port "$cfg" local_port
config_get remote_port "$cfg" remote_port
config_get custom_domains "$cfg" custom_domains
config_get subdomain "$cfg" subdomain
[ -n "$remark" ] || return
echo "" >>$tmpconf
echo "[$remark]" >>$tmpconf
echo "type=$type" >>$tmpconf
[ -n "$local_ip" ] && echo "local_ip=$local_ip" >>$tmpconf
[ -n "$local_port" ] && echo "local_port=$local_port" >>$tmpconf
[ -n "$remote_port" ] && echo "remote_port=$remote_port" >>$tmpconf
[ -n "$custom_domains" ] && echo "custom_domains=$custom_domains" >>$tmpconf
[ -n "$subdomain" ] && echo "subdomain=$subdomain" >>$tmpconf
}
frp_add_cru(){
local time=$1
sed -i '/frp/d' /etc/crontabs/root 2>/dev/null
echo "*/$time * * * * /etc/init.d/frp restart" >> /etc/crontabs/root
}
frp_del_cru(){
sed -i '/frp/d' /etc/crontabs/root 2>/dev/null
}
download_binary(){
echo_date "下载 frpc..."
wget -q https://github.com/fatedier/frp/releases/download/v0.51.3/frp_0.51.3_linux_arm.tar.gz -O /tmp/frp.tar.gz
tar -xf /tmp/frp.tar.gz -C /tmp
cp /tmp/frp_*/frpc /usr/bin/frpc
chmod +x /usr/bin/frpc
rm -rf /tmp/frp*
}
# ================= 核心逻辑 =================
start_service() {
config_load "frp"
local enabled server_addr server_port token time log_level log_max_days
config_get_bool enabled common enabled 1
[ "$enabled" -gt 0 ] || return 1
config_get server_addr common server_addr
config_get server_port common server_port
config_get token common token
config_get log_level common log_level
config_get log_max_days common log_max_days
config_get time common time
mkdir -p /var/etc/frp
[ ! -f "$LOGFILE" ] && touch $LOGFILE
[ ! -f "/usr/bin/frpc" ] && download_binary
[ ! -f "/usr/bin/frpc" ] && return 1
tmpconf="/var/etc/frp/frpc.conf"
echo "[common]" >$tmpconf
echo "server_addr=$server_addr" >>$tmpconf
echo "server_port=$server_port" >>$tmpconf
echo "token=$token" >>$tmpconf
echo "log_file=$LOGFILE" >>$tmpconf
echo "log_level=$log_level" >>$tmpconf
echo "log_max_days=$log_max_days" >>$tmpconf
config_foreach conf_proxy_add proxy "$tmpconf"
[ -f "$tmpconf" ] || return 1
# ===== procd 启动 =====
procd_open_instance
procd_set_param command /usr/bin/frpc -c "$tmpconf"
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
[ "$time" -gt 0 ] && frp_add_cru $time
}
stop_service() {
frp_del_cru
Reduce_Log $LOGFILE
}