#!/bin/bash # 隧道对照 v3:验证「QUIC 失败是因为 MTU」这个假设,并选出最稳的组合。 # # 已查明的事实: # 1. cloudflared 用 QUIC 时反复报 # "failed to dial to edge with quic: timeout: no recent network activity" # 连十几个 IP 才成功一次 —— 握手阶段就掉包。 # 2. 我们给 TCP 做了 MSS 钳制(--set-mss 1400), # 但 **QUIC 走 UDP,TCPMSS 规则完全不生效**。 # 3. 路径 MTU 实测 1472(1500 的包不通)。 # QUIC 默认用 ~1452 字节的包,很容易被中间设备丢掉, # 而 UDP 丢了不会回 ICMP,客户端只能等到超时。 # # 所以对照三组: # A. quic + 6(现状,MTU 1500) —— 预期握手仍不稳 # B. quic + 6 + wlan0 MTU 1400 —— 如果假设成立,握手会变稳 # C. http2 + 8(TCP 吃 MSS 钳制,连接多) —— 对照组 # # 每组记录:连接建齐耗时 / 建齐几条 / 60 次 HTTP 可用率 set -u BASE=https://relay.xxmxxs.top UA='LanRemote/1.8.1' SVC=/etc/systemd/system/lanremote-tunnel.service BACKUP=/root/tunnel.bak.v3.$(date +%s) cp -a "$SVC" "$BACKUP" N=60 set_params() { local proto="$1" conns="$2" cp -a "$BACKUP" "$SVC" sed -i "s/--protocol [a-z0-9]*/--protocol $proto/" "$SVC" sed -i "s/--ha-connections [0-9]*/--ha-connections $conns/" "$SVC" systemctl daemon-reload systemctl restart lanremote-tunnel } set_mtu() { local mtu="$1" ip link set wlan0 mtu "$mtu" 2>/dev/null && echo " wlan0 MTU -> $(cat /sys/class/net/wlan0/mtu)" } # 等连接建齐,返回耗时秒数 wait_conns() { local want="$1" limit="$2" t0 t1 n t0=$(date +%s) while true; do n=$(curl -s --max-time 4 http://127.0.0.1:20241/metrics 2>/dev/null \ | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}') n=${n:-0} t1=$(date +%s) if [ "$n" -ge "$want" ] || [ $((t1 - t0)) -ge "$limit" ]; then echo "$n $((t1 - t0))" return fi sleep 3 done } run_group() { local label="$1" proto="$2" conns="$3" mtu="$4" echo "════════ $label ════════" [ "$mtu" != "keep" ] && set_mtu "$mtu" local mark; mark=$(wc -l < /root/lanremote-relay/tunnel.log 2>/dev/null || echo 0) set_params "$proto" "$conns" local res; res=$(wait_conns "$conns" 150) local n; n=$(echo "$res" | awk '{print $1}') local secs; s=$(echo "$res" | awk '{print $2}') echo " 建齐 $n/$conns 条,用了 ${s} 秒" # 这一轮建立过程中失败了多少次 local dialfail; dialfail=$(tail -n +$((mark+1)) /root/lanremote-relay/tunnel.log 2>/dev/null \ | grep -c 'Failed to dial' || echo 0) echo " 建立期间 dial 失败 $dialfail 次" # 可用率 local ok=0 times=() for i in $(seq 1 "$N"); do out=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 25 \ -H "User-Agent: $UA" "$BASE/v1/health") c=$(echo "$out" | awk '{print $1}') ms=$(echo "$out" | awk '{printf "%d", $2*1000}') if [ "$c" = "200" ]; then ok=$((ok+1)); times+=("$ms"); fi done local pct=$((ok * 100 / N)) local stats="无数据" if [ "${#times[@]}" -gt 0 ]; then stats=$(printf '%s\n' "${times[@]}" | sort -n | awk '{a[NR]=$1} END { printf "中位 %d P90 %d P99 %d 最大 %d", a[int(NR/2)+1], a[int(NR*0.9)+1], a[int(NR*0.99)+1], a[NR]}') fi printf " 可用率 %d%% (%d/%d) 耗时 %s ms\n" "$pct" "$ok" "$N" "$stats" echo "$label|$proto|$conns|$mtu|$n|$s|$dialfail|$pct|$stats" >> /tmp/v3.txt echo } rm -f /tmp/v3.txt echo " 原始 wlan0 MTU: $(cat /sys/class/net/wlan0/mtu)" echo run_group "A. quic+6 MTU1500" quic 6 keep run_group "B. quic+6 MTU1400" quic 6 1400 run_group "C. http2+8 MTU1400" http2 8 keep # 恢复 MTU set_mtu 1500 echo "════════ 汇总 ════════" printf " %-22s %-6s %-4s %-8s %-9s %-8s %s\n" "组" "协议" "连接" "建齐/耗时" "dial失败" "可用率" "延迟" while IFS='|' read -r label proto conns mtu n s dialfail pct stats; do printf " %-22s %-6s %-4s %-8s %-9s %-8s %s\n" \ "$label" "$proto" "$conns" "${n}/${conns} ${s}s" "$dialfail" "${pct}%" "$stats" done < /tmp/v3.txt echo echo "════════ 决策:可用率优先,其次看建齐速度 ════════" BEST=$(sort -t'|' -k8 -rn /tmp/v3.txt | head -1) BL=$(echo "$BEST" | cut -d'|' -f1) BP=$(echo "$BEST" | cut -d'|' -f2) BC=$(echo "$BEST" | cut -d'|' -f3) BM=$(echo "$BEST" | cut -d'|' -f4) echo " 最好: $BL(可用率 $(echo "$BEST" | cut -d'|' -f8)%)" cp -a "$BACKUP" "$SVC" sed -i "s/--protocol [a-z0-9]*/--protocol $BP/" "$SVC" sed -i "s/--ha-connections [0-9]*/--ha-connections $BC/" "$SVC" systemctl daemon-reload systemctl restart lanremote-tunnel echo " 已采用: --protocol $BP --ha-connections $BC" echo " MTU: $(cat /sys/class/net/wlan0/mtu)" echo " 备份: $BACKUP"