Files
ykxmz/_build/test_longpoll.sh
xmz7891 b1119d0da6 修「局域网连接被中转劫持」+ 长轮询 15s→60s
用户报两个问题:
  1. 局域网能搜到电脑,IP/端口/配对码都对,点连接**没有任何反应**
  2. 走中转 1426ms 延时,一会儿就 502

========== 一、局域网被劫持(真 bug)==========

MainActivity.connect() 里:

    boolean usedLan = !relayUrl().isEmpty() ? false : true;
    if (!relay.isEmpty()) {
        reply = client.connectViaRelay(...);      // 走中转
    } else {
        reply = client.connect(host, port, ...);  // 才走局域网
    }

**只看「中转设置里填没填地址」,填了就一律走中转,
完全无视用户填的局域网 IP 和端口。**

而中转单次超时是 40 秒 —— 用户点完就卡在「正在连接…」
四十秒,在他看来就是"没有任何反应"。

影响两条路:
  * 「局域网连接」页面填 IP 直连(connectLanTyped)
  * 首页「已配对的电脑」点一下重连(quickConnect)

修法:给 connect() 加 preferLan 参数。明确要走局域网时
**先直连(6 秒超时,局域网本该是毫秒级),失败再退回中转**,
保证最终能连上。

顺带两个体验改进:
  * 设备列表里,保存的 IP 和手表不同网段时标「⚠ 地址可能已过期」
    (用户截图里那个 192.168.2.64 就是过期地址,电脑早换成
      192.168.10.103 了)
  * quickConnect 判断出地址过期就跳过直连,直接走中转查最新地址 ——
    省掉那 6 秒白等

========== 二、长轮询 15 秒 -> 60 秒 ==========

实测隧道每次请求要多花 1~2 秒(CF 边缘路由 + 回源):

    本机直连 60 秒的 poll  ->  60.0s
    走公网同一个 poll      ->  61.5~62.0s

这个开销是**按次数**收的:
    15 秒一循环 -> 一天 5760 次/端,光路费 2.4~3.2 小时
    60 秒一循环 -> 一天 1440 次/端,光费 0.6~0.8 小时
而且请求越频繁,撞上丢包/重连窗口的概率越大。

**不会让命令变慢**:长轮询是"有数据立刻返回"(device.event 唤醒),
拉长只是减少空转重开。实测 60 秒长轮询穿隧道 4/4 通过;
Cloudflare 单请求上限 100 秒,留了 40 秒余量。

三层同步改(客户端超时必须显著大于服务端 hold):
  * relay/relay.py                    RELAY_POLL 默认 25 -> 60
  * windows/lanremote/relay.py        POLL_TIMEOUT 40 -> 90
  * android .../RelayTransport.java   POLL_TIMEOUT_MS 40000 -> 90000

========== 三、A/B 实测(还在跑更严谨的一轮)==========

短测发现基线 http2+4 虽然 100% 成功,但 **P90 是 16.8 秒**
(每 10 次有 1 次卡十几秒 —— 正是用户说的"没反应"),
而 quic+6 的 P90 只有 2.8 秒。正在用真实长轮询模式
跑 10 分钟对照确认。
2026-10-07 14:11:33 +08:00

79 lines
3.3 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 验证长轮询能不能穿过 Cloudflare 隧道跑满 60 秒。
#
# 为什么要先验:
# Cloudflare 对单个 HTTP 请求有 **100 秒**上限,超了返回 524。
# 我们打算把长轮询从 15 秒拉到 60 秒 ——
# 如果隧道/CF 那边撑不住,agent 会一直断线重连,反而更糟。
# 所以先拿真实的 poll 接口量一遍。
#
# 做法:
# 1. 注册一台临时设备,拿到 token
# 2. 用 token 打 /v1/agent/poll(公网地址),量它挂了多久
# 3. 对比本机直连和中转两条路
set -u
BASE=https://relay.xxmxxs.top
LOCAL=http://127.0.0.1:8080
UA='LanRemote/1.8.0'
echo "════════ 1. 注册一台临时设备 ════════"
DEV="probe-$(date +%s)"
REG=$(curl -s --max-time 30 -X POST "$BASE/v1/agent/register" \
-H 'Content-Type: application/json' \
-H "User-Agent: $UA" \
-d "{\"deviceId\":\"$DEV\",\"code\":\"999999\",\"name\":\"poll-probe\",\"version\":\"1.8.0\"}")
echo " $REG" | head -c 200
echo
TOKEN=$(echo "$REG" | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null)
if [ -z "$TOKEN" ]; then
echo " [失败] 没拿到 token,后面的测试没法做"
exit 1
fi
echo " token 长度 ${#TOKEN}"
echo
echo "════════ 2. 本机直连 poll(基准,应等于服务端设定的秒数)════════"
for i in 1 2; do
out=$(curl -s -o /tmp/poll_local.bin -w '%{http_code} %{time_total}' \
--max-time 120 -X POST "$LOCAL/v1/agent/poll" \
-H "X-Token: $TOKEN" -H "User-Agent: $UA")
size=$(wc -c < /tmp/poll_local.bin 2>/dev/null || echo 0)
echo " 第 $i 次: 码/耗时 = $(echo "$out" | awk '{printf "%s %.1fs", $1, $2}') 返回 $size 字节"
done
echo
echo "════════ 3. 走公网(Cloudflare 隧道)poll ════════"
echo " 这是关键:能不能挂满 60 秒不断"
ok=0
for i in 1 2 3; do
out=$(curl -s -o /tmp/poll_pub.bin -w '%{http_code} %{time_total}' \
--max-time 130 -X POST "$BASE/v1/agent/poll" \
-H "X-Token: $TOKEN" -H "User-Agent: $UA")
code=$(echo "$out" | awk '{print $1}')
secs=$(echo "$out" | awk '{printf "%.1f", $2}')
size=$(wc -c < /tmp/poll_pub.bin 2>/dev/null || echo 0)
mark=""
[ "$code" = "200" ] && mark="✓"
[ "$code" = "524" ] && mark="**CF 超时了**"
printf " 第 %d 次: 码 %s 耗时 %ss 返回 %s 字节 %s\n" "$i" "$code" "$secs" "$size" "$mark"
[ "$code" = "200" ] && ok=$((ok + 1))
sleep 1
done
echo " 成功 $ok/3"
echo
echo "════════ 4. 服务端当前的长轮询设定 ════════"
grep -E '^REMOTE_POLL|Environment=RELAY_POLL' /etc/systemd/system/lanremote-relay.service 2>/dev/null | sed 's/^/ /'
echo " 实际生效: $(curl -s --max-time 10 $LOCAL/v1/health | python3 -c 'import sys,json; d=json.load(sys.stdin); print("(health 没报这个字段)")' 2>/dev/null)"
tail -40 /root/lanremote-relay/relay.log 2>/dev/null | grep '长轮询' | tail -1 | sed 's/^/ /'
echo
echo "════════ 5. 清掉临时设备 ════════"
KEY=$(cat /root/lanremote-relay/data/admin_key.txt 2>/dev/null)
curl -s --max-time 20 -X POST "$LOCAL/v1/admin/delete" \
-H "X-Admin: $KEY" -H 'Content-Type: application/json' \
-d "{\"deviceId\":\"$DEV\"}" | head -c 120
echo