大家不妨想象这样一个场景,当你科学上网时,有时浏览网站 A,需要经过节点 a,有时浏览网站 B,需要经过节点 b,ab 为不同 IP 区域的节点。如果把分流规则写在代理配置里,那么每次更新代理订阅时都会被覆盖。因此,请往下看看我的解决方案吧。

大体逻辑

graph TD
  A[浏览器/被代理软件] --> B{代理拓展 如ZeroOmega}
  B -- 端口1 --> C[网站A]
  B -- 端口2 --> D[网站B]
  B -- 端口3 --> E[网站C]
  B -- 端口4 --> F[网站D]
  B -- 直连 --> G[网站E]

其中端口 n 是来自 sing-box 给每个节点分配的端口。

sing-box 多端口映射配置

下面是模板,一些需要改的东西如下。注意其中不包含分流规则

  1. tag:入 / 出站标签,用于区分不同的入站端口。
  2. listen_port:入站端口,即在被代理软件中填的端口。
  3. type:入 / 出站类型,一般入站用 mixed,出站根据自己的机场配置修改。
  4. outbounds:出站配置,根据自己的机场配置修改。
  5. route:分流规则,建议一个入站端口对应一个出站端口就行了,tagtag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
"log": {
"level": "info"
},
"inbounds": [
{
"type": "mixed",
"tag": "in_0",
"listen": "0.0.0.0",
"listen_port": 20000
},
{
"type": "mixed",
"tag": "in_1",
"listen": "0.0.0.0",
"listen_port": 20001
},
{
"type": "mixed",
"tag": "in_2",
"listen": "0.0.0.0",
"listen_port": 20002
}
}
],
"outbounds": [
{
"type": "vmess",
"tag": "out_1",
"server": "example.com",
"server_port": 10000,
"uuid": "xxx",
"security": "auto",
"alter_id": 0,
"transport": {
"type": "ws",
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
},
{
"type": "vmess",
"tag": "out_2",
"server": "example.com",
"server_port": 10000,
"uuid": "xxx",
"security": "auto",
"alter_id": 0,
"transport": {
"type": "ws",
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
},
{
"type": "vmess",
"tag": "out_3",
"server": "example.com",
"server_port": 10000,
"uuid": "xxx",
"security": "auto",
"alter_id": 0,
"transport": {
"type": "ws",
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}
],
"route": {
"rules": [
{
"inbound": "in_0",
"outbound": "out_1"
},
{
"inbound": "in_1",
"outbound": "out_2"
},
{
"inbound": "in_2",
"outbound": "out_3"
}
]
}
}

Clash 配置转 sing-box 多端口映射配置

注意转换后会丢失原来的分流规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import requests
import yaml
import json
import os

# 改成自己的机场订阅链接
YAML_URL = "https://example.com/clash.yaml"
OUTPUT_JSON = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
# 节点备注 会在下一节测试节点可用性时使用
OUTPUT_NAME = os.path.join(os.path.dirname(os.path.abspath(__file__)), "proxy.csv")
# 起始端口
START_PORT = 20000

# 拉取YAML
resp = requests.get(YAML_URL, timeout=15)
resp.raise_for_status()
yaml_data = yaml.safe_load(resp.text)

proxies = yaml_data.get("proxies", [])
if not proxies:
raise RuntimeError("YAML中未找到proxies")

inbounds = []
outbounds = []
routes = []
tmp = ""

for idx, p in enumerate(proxies):
port = START_PORT + idx
in_tag = f"in_{idx}"
out_tag = p["name"]

# inbound
inbounds.append({
"type": "mixed",
"tag": in_tag,
"listen": "0.0.0.0",
"listen_port": port
})

# outbound 务必对照自己的配置确认
ptype = p.get("type")

if ptype == "vmess":
outbound = {
"type": "vmess",
"tag": out_tag,
"server": p["server"],
"server_port": int(p["port"]),
"uuid": p["uuid"],
"security": "auto",
"alter_id": 0,
"transport": {
"type": "ws",
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}

elif ptype == "vless":
outbound = {
"type": "vless",
"tag": out_tag,
"server": p["server"],
"server_port": int(p["port"]),
"uuid": p["uuid"],
"security": "auto",
"alter_id": 0,
"transport": {
"type": "ws",
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}

elif ptype == "trojan":
outbound = {
"type": "trojan",
"tag": out_tag,
"server": p["server"],
"server_port": int(p["port"]),
"password": p["password"],
"security": "auto"
}

elif ptype == "ss":
outbound = {
"type": "shadowsocks",
"tag": out_tag,
"server": p["server"],
"server_port": int(p["port"]),
"method": p["cipher"],
"password": p["password"]
}

else:
# 不支持的类型,直接跳过(inbound/outbound保持一致)
continue

tmp += f"{in_tag},{port},{out_tag}".strip() + "\n"

outbounds.append(outbound)

# route
routes.append({
"inbound": in_tag,
"outbound": out_tag
})

# sing-box配置
config = {
"log": {
"level": "info"
},
"inbounds": inbounds,
"outbounds": outbounds,
"route": {
"rules": routes
}
}

with open(OUTPUT_JSON, "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)

with open(OUTPUT_NAME, "w", encoding="utf-8") as f:
f.write(tmp)

# 打印每个端口和对应备注 格式{in_tag},{port},{out_tag}
print(tmp)
print(f"端口范围:{START_PORT}{START_PORT + len(outbounds) - 1}")

测试节点可用性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env bash

# 节点备注 路径自己修改下
CSV_FILE="/proxy.csv"
TARGET_URL="https://www.google.com"
# 超时时间
TIMEOUT=5
# 并发数
MAX_JOBS=10

TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT

echo "PORT | REMARK | TLS(ms) | STATUS"

job_count=0

run_test() {
local port="$1"
local remark="$2"
local outfile="$TMP_DIR/$port.txt"

result=$(curl -o /dev/null -s \
--socks5-hostname 127.0.0.1:$port \
--connect-timeout $TIMEOUT \
-w "%{time_appconnect}" \
"$TARGET_URL")

if [ $? -eq 0 ]; then
tls_ms=$(awk -v t="$result" 'BEGIN{printf "%.0f", t*1000}')

echo "$port|${remark:0:10}|$tls_ms|OK" > "$outfile"
else
echo "$port|${remark:0:10}| - |NG" > "$outfile"
fi
}

while IFS=',' read -r inbound port remark; do
[ -z "$port" ] && continue

run_test "$port" "$remark" &

((job_count++))

if (( job_count >= MAX_JOBS )); then
wait -n
((job_count--))
fi

done < "$CSV_FILE"

wait

cat "$TMP_DIR"/*.txt | sort -n -t '|' -k1 | column -t -s '|'

网站地图 | 状态监测 | 皮带张力测试 | File Server | 博友圈 | 博客说
Copyright 2022-2026 | Powered by Hexo 7.3.0 & Stellar 1.33.1
总访问量次 |