全自动跑(推荐):
1# 扫描 → TCP压测 → 自动找HTTP端口上传200GB
2python3 autostress.py 192.168.1.100 --http-size 200
只做 HTTP 大包上传:
1# 直接指定端口,跳过扫描
2python3 autostress.py 192.168.1.100 --http-only --http-port 80 --http-size 200
调大并发和包体:
1python3 autostress.py 192.168.1.100 --http-only --http-port 80 \
2 --http-size 200 --http-concurrency 20 --chunk-mb 64
三个阶段说明:
| 阶段 | 内容 | 需要对方配合? |
|---|---|---|
| ① 端口扫描 | 发现开放端口 | ❌ 不需要 |
| ② TCP压测 | QPS / 延迟 / 成功率 | ❌ 不需要 |
| ③ HTTP大包上传 | 吞吐量 / 断连 / 稳定性 | ❌ 不需要(对方有HTTP服务即可) |
HTTP 大包上传的原理:向目标的 HTTP 服务 POST 大块数据,服务器返回 4xx/5xx 也没关系——数据已经从你这边发出去了,吞吐量照常统计。
1nano /root/autostress.py
1#!/usr/bin/env python3
2"""
3自动扫描 + 压测工具 v2.0
41. 扫描目标 IP 的开放端口
52. TCP 连接压测(测 QPS / 延迟 / 稳定性)
63. HTTP 大包上传压测(测吞吐量,无需目标配合接收端)
7"""
8
9import socket
10import time
11import threading
12import argparse
13import sys
14import os
15import statistics
16import ssl
17import urllib.request
18import urllib.error
19from collections import Counter
20from concurrent.futures import ThreadPoolExecutor, as_completed
21from dataclasses import dataclass, field
22from typing import List, Tuple, Optional
23
24# ──────────────────────────────────────────────
25# 颜色
26# ──────────────────────────────────────────────
27R = "\033[0m"
28BOLD = "\033[1m"
29GREEN = "\033[92m"
30RED = "\033[91m"
31YELLOW = "\033[93m"
32CYAN = "\033[96m"
33GRAY = "\033[90m"
34BLUE = "\033[94m"
35
36def c(text, *codes): return "".join(codes) + str(text) + R
37
38# ──────────────────────────────────────────────
39# 常量
40# ──────────────────────────────────────────────
41COMMON_PORTS = {
42 21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
43 53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
44 443: "HTTPS", 445: "SMB", 3306: "MySQL", 3389: "RDP",
45 5432: "PostgreSQL", 5672: "RabbitMQ", 6379: "Redis",
46 8080: "HTTP-Alt", 8443: "HTTPS-Alt", 9200: "Elasticsearch",
47 27017: "MongoDB", 11211: "Memcached",
48}
49HTTP_PORTS = {80, 443, 8080, 8443, 8000, 8008, 8081, 8088, 8888, 9000, 9090}
50GB = 1024 ** 3
51
52# ──────────────────────────────────────────────
53# 工具函数
54# ──────────────────────────────────────────────
55def make_payload(size: int) -> bytes:
56 """动态生成随机数据块,不占磁盘"""
57 base = os.urandom(min(size, 65536))
58 repeat = size // len(base)
59 remainder = size % len(base)
60 return base * repeat + base[:remainder]
61
62def fmt_size(b: float) -> str:
63 if b >= GB: return f"{b/GB:.2f} GB"
64 if b >= 1024**2: return f"{b/1024**2:.2f} MB"
65 if b >= 1024: return f"{b/1024:.2f} KB"
66 return f"{b:.0f} B"
67
68def fmt_speed(bps: float) -> str:
69 mbps = bps / 1024 / 1024
70 if mbps >= 1000: return f"{mbps/1024:.2f} GB/s"
71 return f"{mbps:.2f} MB/s"
72
73# ──────────────────────────────────────────────
74# 阶段 1:端口扫描
75# ──────────────────────────────────────────────
76def scan_port(host: str, port: int, timeout: float) -> Tuple[int, bool, float]:
77 start = time.perf_counter()
78 try:
79 with socket.create_connection((host, port), timeout=timeout):
80 return port, True, (time.perf_counter() - start) * 1000
81 except Exception:
82 return port, False, 0.0
83
84
85def scan_ports(host: str, ports: List[int], timeout: float = 1.0, workers: int = 800) -> List[Tuple[int, float]]:
86 open_ports = []
87 total = len(ports)
88 done = 0
89 lock = threading.Lock()
90
91 print(c(f"\n 🔍 正在扫描 {host} 的 {total} 个端口...", CYAN))
92
93 with ThreadPoolExecutor(max_workers=workers) as ex:
94 futures = {ex.submit(scan_port, host, p, timeout): p for p in ports}
95 for future in as_completed(futures):
96 port, is_open, lat = future.result()
97 with lock:
98 done += 1
99 if is_open:
100 open_ports.append((port, lat))
101 bar_len = 40
102 filled = int(bar_len * done / total)
103 bar = c("█" * filled, GREEN) + c("░" * (bar_len - filled), GRAY)
104 print(f"\r [{bar}] {done}/{total}", end="", flush=True)
105
106 print()
107 return sorted(open_ports)
108
109
110def print_scan_result(host: str, open_ports: List[Tuple[int, float]]):
111 sep = c("─" * 60, GRAY)
112 print()
113 print(sep)
114 print(c(f" 🗺 端口扫描结果 → {host}", BOLD, CYAN))
115 print(sep)
116 if not open_ports:
117 print(c(" 未发现任何开放端口", RED))
118 else:
119 print(f" {'端口':<8} {'服务':<16} {'连接延迟':<12} {'类型'}")
120 print(c(" " + "·" * 46, GRAY))
121 for port, lat in open_ports:
122 svc = COMMON_PORTS.get(port, "Unknown")
123 lat_color = GREEN if lat < 10 else YELLOW if lat < 50 else RED
124 ptype = c("HTTP ✓", CYAN) if port in HTTP_PORTS else c("TCP", GRAY)
125 print(f" {c(str(port), CYAN):<18} {svc:<16} {c(f'{lat:.1f}ms', lat_color):<20} {ptype}")
126 print(sep)
127
128
129# ──────────────────────────────────────────────
130# 阶段 2:TCP 连接压测
131# ──────────────────────────────────────────────
132@dataclass
133class BenchResult:
134 port: int
135 service: str
136 total: int
137 success: int = 0
138 latencies: List[float] = field(default_factory=list)
139 errors: List[str] = field(default_factory=list)
140 duration: float = 0.0
141
142 @property
143 def fail(self): return self.total - self.success
144 @property
145 def success_rate(self): return self.success / self.total * 100 if self.total else 0
146 @property
147 def avg_lat(self): return statistics.mean(self.latencies) if self.latencies else 0
148 @property
149 def min_lat(self): return min(self.latencies) if self.latencies else 0
150 @property
151 def max_lat(self): return max(self.latencies) if self.latencies else 0
152 @property
153 def p95_lat(self):
154 if not self.latencies: return 0
155 s = sorted(self.latencies)
156 return s[int(len(s) * 0.95)]
157 @property
158 def qps(self): return self.success / self.duration if self.duration else 0
159
160
161def _tcp_task(host, port, timeout):
162 start = time.perf_counter()
163 try:
164 with socket.create_connection((host, port), timeout=timeout):
165 return True, (time.perf_counter() - start) * 1000, None
166 except Exception as e:
167 return False, (time.perf_counter() - start) * 1000, type(e).__name__
168
169
170def bench_port(host, port, requests, concurrency, timeout) -> BenchResult:
171 service = COMMON_PORTS.get(port, "Unknown")
172 result = BenchResult(port=port, service=service, total=requests)
173 lock = threading.Lock()
174 done = [0]
175
176 print(c(f"\n ⚡ TCP压测 端口 {port} ({service}) {requests}次 / {concurrency}并发", BOLD))
177
178 t0 = time.perf_counter()
179 with ThreadPoolExecutor(max_workers=concurrency) as ex:
180 futures = [ex.submit(_tcp_task, host, port, timeout) for _ in range(requests)]
181 for future in as_completed(futures):
182 ok, lat, err = future.result()
183 with lock:
184 done[0] += 1
185 if ok:
186 result.success += 1
187 result.latencies.append(lat)
188 elif err:
189 result.errors.append(err)
190 bar_len = 35
191 filled = int(bar_len * done[0] / requests)
192 bar = c("█" * filled, CYAN) + c("░" * (bar_len - filled), GRAY)
193 pct = done[0] / requests * 100
194 print(f"\r [{bar}] {pct:5.1f}% ✓{result.success} ✗{result.fail}", end="", flush=True)
195
196 result.duration = time.perf_counter() - t0
197 print()
198 return result
199
200
201def print_tcp_bench_report(results: List[BenchResult], host: str):
202 sep = c("─" * 60, GRAY)
203 print()
204 print(sep)
205 print(c(f" 📊 TCP 压测报告 → {host}", BOLD, CYAN))
206 print(sep)
207
208 for r in results:
209 sr_color = GREEN if r.success_rate >= 95 else YELLOW if r.success_rate >= 70 else RED
210 print()
211 print(c(f" 端口 {r.port} ({r.service})", BOLD))
212 print(f" {'成功率':<14} {c(f'{r.success_rate:.1f}%', sr_color)} ({r.success}/{r.total})")
213 print(f" {'QPS':<14} {c(f'{r.qps:.1f}', YELLOW)}")
214 print(f" {'总耗时':<14} {r.duration:.2f}s")
215 if r.latencies:
216 print(f" {'延迟(ms)':<14} min={r.min_lat:.1f} avg={c(f'{r.avg_lat:.1f}', CYAN)} p95={r.p95_lat:.1f} max={r.max_lat:.1f}")
217 if r.errors:
218 top = Counter(r.errors).most_common(3)
219 errs = " ".join(f"{n}x {e}" for e, n in top)
220 print(c(f" 错误: {errs}", RED))
221 grade = (c("✅ 优秀", GREEN) if r.success_rate >= 99 else
222 c("🟡 良好", YELLOW) if r.success_rate >= 90 else
223 c("⚠️ 较差", YELLOW) if r.success_rate >= 50 else
224 c("❌ 不可用", RED))
225 print(f" {'评级':<14} {grade}")
226
227 print()
228 print(sep)
229
230
231# ──────────────────────────────────────────────
232# 阶段 3:HTTP 大包上传压测
233# ──────────────────────────────────────────────
234@dataclass
235class HttpBulkResult:
236 port: int
237 url: str
238 target_bytes: int
239 sent_bytes: int = 0
240 success_reqs: int = 0
241 fail_reqs: int = 0
242 total_reqs: int = 0
243 latencies: List[float] = field(default_factory=list)
244 throughputs: List[float] = field(default_factory=list)
245 errors: List[str] = field(default_factory=list)
246 duration: float = 0.0
247 disconnects: int = 0
248
249 @property
250 def avg_throughput(self): return statistics.mean(self.throughputs) if self.throughputs else 0
251 @property
252 def peak_throughput(self): return max(self.throughputs) if self.throughputs else 0
253 @property
254 def success_rate(self): return self.success_reqs / self.total_reqs * 100 if self.total_reqs else 0
255 @property
256 def overall_mbps(self): return (self.sent_bytes / self.duration / 1024 / 1024) if self.duration else 0
257
258
259def _http_upload_task(url: str, payload: bytes, timeout: int, ssl_ctx) -> Tuple[bool, float, float, str]:
260 """
261 单次 HTTP POST 上传。
262 服务器返回 4xx/5xx 也算"数据已发出",统计吞吐。
263 """
264 start = time.perf_counter()
265 try:
266 req = urllib.request.Request(url, data=payload, method="POST")
267 req.add_header("Content-Type", "application/octet-stream")
268 req.add_header("User-Agent", "BulkStressTest/2.0")
269 req.add_header("Content-Length", str(len(payload)))
270 try:
271 with urllib.request.urlopen(req, timeout=timeout, context=ssl_ctx) as resp:
272 resp.read(1024)
273 except urllib.error.HTTPError:
274 pass # 4xx/5xx:数据已发出,继续统计
275
276 elapsed = time.perf_counter() - start
277 mbps = len(payload) / elapsed / 1024 / 1024
278 return True, elapsed * 1000, mbps, ""
279 except Exception as e:
280 elapsed_ms = (time.perf_counter() - start) * 1000
281 return False, elapsed_ms, 0.0, str(e)[:100]
282
283
284def http_bulk_stress(host, port, total_gb, chunk_mb, concurrency, timeout) -> HttpBulkResult:
285 scheme = "https" if port in (443, 8443) else "http"
286 url = f"{scheme}://{host}:{port}/"
287
288 ssl_ctx = ssl.create_default_context()
289 ssl_ctx.check_hostname = False
290 ssl_ctx.verify_mode = ssl.CERT_NONE
291
292 target_bytes = int(total_gb * GB)
293 chunk_bytes = chunk_mb * 1024 * 1024
294 total_reqs = max(1, int(target_bytes / chunk_bytes))
295
296 result = HttpBulkResult(port=port, url=url, target_bytes=target_bytes, total_reqs=total_reqs)
297 lock = threading.Lock()
298 done = [0]
299
300 print(c(f"\n 📤 HTTP 大包上传压测", BOLD))
301 print(c(f" URL: {url}", GRAY))
302 print(c(f" 目标: {fmt_size(target_bytes)} ({total_reqs} 个请求 × {chunk_mb}MB)", GRAY))
303 print(c(f" 并发: {concurrency} 超时: {timeout}s", GRAY))
304 print()
305
306 # 预生成 payload(所有线程共享同一块,不重复分配)
307 print(c(" ⏳ 生成数据包...", GRAY), end=" ", flush=True)
308 payload = make_payload(chunk_bytes)
309 print(c(f"完成 ({fmt_size(len(payload))})", GREEN))
310 print()
311
312 def reporter():
313 while done[0] < total_reqs and result.duration == 0:
314 time.sleep(1.5)
315 with lock:
316 sent = result.sent_bytes
317 cur = done[0]
318 fail = result.fail_reqs
319 elapsed = time.perf_counter() - t0
320 if elapsed <= 0 or sent <= 0:
321 continue
322 spd = sent / elapsed / 1024 / 1024
323 pct = sent / target_bytes * 100
324 eta_s = int((target_bytes - sent) / (sent / elapsed))
325 eta_str = f"{eta_s//3600}h{(eta_s%3600)//60}m" if eta_s > 3600 else \
326 f"{eta_s//60}m{eta_s%60:02d}s" if eta_s > 60 else f"{eta_s}s"
327 bar_len = 32
328 filled = int(bar_len * min(pct, 100) / 100)
329 bar = c("█" * filled, GREEN) + c("░" * (bar_len - filled), GRAY)
330 print(
331 f"\r [{bar}] {pct:5.1f}% "
332 f"{c(fmt_size(sent), CYAN)}/{fmt_size(target_bytes)} "
333 f"速度:{c(f'{spd:.1f}MB/s', YELLOW)} "
334 f"ETA:{eta_str} "
335 f"失败:{c(str(fail), RED if fail else GRAY)}",
336 end="", flush=True
337 )
338
339 t0 = time.perf_counter()
340 rep = threading.Thread(target=reporter, daemon=True)
341 rep.start()
342
343 with ThreadPoolExecutor(max_workers=concurrency) as ex:
344 futures = [ex.submit(_http_upload_task, url, payload, timeout, ssl_ctx) for _ in range(total_reqs)]
345 for future in as_completed(futures):
346 ok, lat_ms, mbps, err = future.result()
347 with lock:
348 done[0] += 1
349 if ok:
350 result.success_reqs += 1
351 result.sent_bytes += chunk_bytes
352 result.latencies.append(lat_ms)
353 result.throughputs.append(mbps)
354 else:
355 result.fail_reqs += 1
356 result.errors.append(err)
357 err_l = err.lower()
358 if any(k in err_l for k in ("reset", "broken", "connect", "refused", "timed")):
359 result.disconnects += 1
360
361 result.duration = time.perf_counter() - t0
362 print()
363 return result
364
365
366def print_http_bulk_report(r: HttpBulkResult):
367 sep = c("─" * 60, GRAY)
368 print()
369 print(sep)
370 print(c(f" 📦 HTTP 大包上传报告 → {r.url}", BOLD, CYAN))
371 print(sep)
372 print()
373
374 sent_pct = r.sent_bytes / r.target_bytes * 100 if r.target_bytes else 0
375 print(f" {'发送总量':<16} {c(fmt_size(r.sent_bytes), GREEN)} ({sent_pct:.1f}% of {fmt_size(r.target_bytes)})")
376 print(f" {'总耗时':<16} {r.duration:.2f}s")
377 print(f" {'整体吞吐量':<16} {c(fmt_speed(r.sent_bytes / r.duration) if r.duration else '0', YELLOW)}")
378 print(f" {'请求成功率':<16} {c(f'{r.success_rate:.1f}%', GREEN if r.success_rate >= 95 else YELLOW)} ({r.success_reqs}/{r.total_reqs})")
379 print()
380
381 if r.throughputs:
382 avg_tp = statistics.mean(r.throughputs)
383 peak = max(r.throughputs)
384 med = statistics.median(r.throughputs)
385 low = min(r.throughputs)
386 print(f" {'单请求吞吐(MB/s)':<16}")
387 print(f" {'平均':<12} {c(f'{avg_tp:.2f}', CYAN)}")
388 print(f" {'中位数':<12} {med:.2f}")
389 print(f" {'峰值':<12} {c(f'{peak:.2f}', GREEN)}")
390 print(f" {'最低':<12} {low:.2f}")
391 print()
392
393 if r.latencies:
394 sl = sorted(r.latencies)
395 print(f" {'请求延迟(ms)':<16}")
396 print(f" {'平均':<12} {statistics.mean(r.latencies):.0f}")
397 print(f" {'P95':<12} {sl[int(len(sl)*0.95)]:.0f}")
398 print(f" {'P99':<12} {sl[int(len(sl)*0.99)]:.0f}")
399 print(f" {'最大':<12} {max(r.latencies):.0f}")
400 print()
401
402 print(f" {'断连/重置':<16} {c(str(r.disconnects), RED if r.disconnects else GREEN)}")
403
404 if r.errors:
405 print()
406 print(c(" 错误摘要 (TOP 5):", YELLOW))
407 for err, cnt in Counter(r.errors).most_common(5):
408 print(f" [{cnt}x] {err}")
409
410 print()
411 print(c(" 稳定性评估:", BOLD))
412 if r.success_rate >= 99 and r.disconnects == 0:
413 print(c(" ✅ 优秀 — 全程无断连,吞吐稳定", GREEN))
414 elif r.success_rate >= 90:
415 print(c(f" 🟡 良好 — 成功率 {r.success_rate:.1f}%,轻微波动", YELLOW))
416 elif r.success_rate >= 60:
417 print(c(f" ⚠️ 较差 — 成功率 {r.success_rate:.1f}%,服务器可能限流或过载", YELLOW))
418 else:
419 print(c(f" ❌ 不可用 — 大量请求失败({r.fail_reqs}/{r.total_reqs}),检查目标服务", RED))
420
421 print()
422 print(sep)
423 print()
424
425
426# ──────────────────────────────────────────────
427# CLI
428# ──────────────────────────────────────────────
429def parse_args():
430 parser = argparse.ArgumentParser(
431 description="自动扫描 + TCP压测 + HTTP大包上传压测(无需目标配合)",
432 formatter_class=argparse.RawDescriptionHelpFormatter,
433 epilog="""
434示例:
435 # 全自动:扫描 → TCP压测 → HTTP大包上传(默认1GB)
436 python3 autostress.py 192.168.1.100
437
438 # 发送 200GB HTTP 大包(自动找HTTP端口)
439 python3 autostress.py 192.168.1.100 --http-size 200
440
441 # 只做 HTTP 大包上传,指定端口
442 python3 autostress.py 192.168.1.100 --http-only --http-port 8080 --http-size 10
443
444 # 大并发大包:20并发 × 64MB 单包 × 200GB
445 python3 autostress.py 192.168.1.100 --http-only --http-port 80 \\
446 --http-size 200 --http-concurrency 20 --chunk-mb 64
447
448 # 只扫描端口,不压测
449 python3 autostress.py 192.168.1.100 --scan-only
450
451 # 全端口扫描
452 python3 autostress.py 192.168.1.100 --full-scan
453 """
454 )
455 parser.add_argument("host", help="目标 IP 或域名")
456
457 scan = parser.add_argument_group("扫描参数")
458 scan.add_argument("--full-scan", action="store_true", help="扫描全部 65535 端口")
459 scan.add_argument("--ports", type=str, default=None, help="手动指定端口,如 80,443 或 1-1024")
460 scan.add_argument("--scan-only", action="store_true", help="只扫描端口,不压测")
461 scan.add_argument("--scan-timeout", type=float, default=0.8, help="扫描超时(默认0.8s)")
462 scan.add_argument("--scan-workers", type=int, default=800, help="扫描并发数(默认800)")
463
464 tcp = parser.add_argument_group("TCP压测参数")
465 tcp.add_argument("-n", "--requests", type=int, default=200, help="每端口TCP请求数(默认200)")
466 tcp.add_argument("-c", "--concurrency", type=int, default=20, help="TCP并发数(默认20)")
467 tcp.add_argument("--bench-timeout", type=int, default=5, help="TCP连接超时(默认5s)")
468 tcp.add_argument("--max-bench-ports", type=int, default=3, help="最多TCP压测端口数(默认3)")
469
470 http = parser.add_argument_group("HTTP大包上传参数")
471 http.add_argument("--http-only", action="store_true",
472 help="跳过扫描和TCP压测,直接做HTTP大包上传")
473 http.add_argument("--http-port", type=int, default=None,
474 help="HTTP上传目标端口(默认自动检测)")
475 http.add_argument("--http-size", type=float, default=1.0,
476 help="上传总量 GB(默认1GB,大测试用200)")
477 http.add_argument("--chunk-mb", type=int, default=32,
478 help="单次上传包大小 MB(默认32MB)")
479 http.add_argument("--http-concurrency", type=int, default=10,
480 help="HTTP上传并发连接数(默认10)")
481 http.add_argument("--http-timeout", type=int, default=60,
482 help="HTTP请求超时(默认60s)")
483 http.add_argument("--no-http", action="store_true",
484 help="跳过HTTP大包上传测试")
485
486 return parser.parse_args()
487
488
489def resolve_ports(args) -> List[int]:
490 if args.ports:
491 ports = []
492 for seg in args.ports.split(","):
493 seg = seg.strip()
494 if "-" in seg:
495 a, b = seg.split("-")
496 ports.extend(range(int(a), int(b) + 1))
497 else:
498 ports.append(int(seg))
499 return ports
500 elif args.full_scan:
501 return list(range(1, 65536))
502 else:
503 return sorted(set(list(COMMON_PORTS.keys()) + [
504 8000, 8008, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089,
505 8090, 8888, 9000, 9090, 9091, 9092, 9100, 9300, 9999,
506 4000, 4001, 4040, 4200, 4443, 4500,
507 2181, 2375, 2376, 2379, 2380,
508 5000, 5001, 5601, 5900,
509 6000, 6001, 6443, 6800,
510 7000, 7001, 7002, 7070, 7077, 7474,
511 10000, 10001, 10250, 10255,
512 15672, 16379, 50070,
513 ]))
514
515
516def main():
517 args = parse_args()
518 host = args.host
519
520 print()
521 print(c(" ╔══════════════════════════════════════════════╗", CYAN))
522 print(c(" ║ 🛰 自动扫描 + TCP + HTTP大包压测 v2.0 ║", CYAN, BOLD))
523 print(c(" ╚══════════════════════════════════════════════╝", CYAN))
524 print()
525 print(c(f" 目标: {host}", BOLD))
526
527 try:
528 ip = socket.gethostbyname(host)
529 if ip != host:
530 print(c(f" 解析: {ip}", GRAY))
531 except Exception as e:
532 print(c(f" ❌ DNS 解析失败: {e}", RED))
533 sys.exit(1)
534
535 # ── HTTP-only 模式 ──
536 if args.http_only:
537 port = args.http_port or 80
538 print(c(f"\n [HTTP-only 模式] 端口 {port} 上传 {args.http_size}GB", YELLOW))
539 r = http_bulk_stress(
540 host=host, port=port,
541 total_gb=args.http_size,
542 chunk_mb=args.chunk_mb,
543 concurrency=args.http_concurrency,
544 timeout=args.http_timeout,
545 )
546 print_http_bulk_report(r)
547 return
548
549 # ── 全自动模式 ──
550 ports = resolve_ports(args)
551 print(c(f" 扫描端口数: {len(ports)}", GRAY))
552
553 # 阶段1:扫描
554 open_ports = scan_ports(host, ports, timeout=args.scan_timeout, workers=args.scan_workers)
555 print_scan_result(host, open_ports)
556
557 if not open_ports:
558 print(c(" 没有找到开放端口,退出。", RED))
559 print(c(" 提示:防火墙可能拦截了扫描,试试 --scan-timeout 2", GRAY))
560 sys.exit(0)
561
562 if args.scan_only:
563 sys.exit(0)
564
565 # 阶段2:TCP 压测
566 bench_targets = [p for p, _ in open_ports[:args.max_bench_ports]]
567 skipped = [str(p) for p, _ in open_ports[args.max_bench_ports:]]
568 if skipped:
569 print(c(f"\n ℹ️ TCP只压测前 {args.max_bench_ports} 个端口,其余跳过: {', '.join(skipped)}", GRAY))
570
571 print(c(f"\n ── 阶段2:TCP 连接压测 ──", BOLD))
572 tcp_results = []
573 for port in bench_targets:
574 r = bench_port(host, port, args.requests, args.concurrency, args.bench_timeout)
575 tcp_results.append(r)
576 print_tcp_bench_report(tcp_results, host)
577
578 # 阶段3:HTTP 大包上传
579 if args.no_http:
580 return
581
582 http_port = args.http_port
583 if not http_port:
584 for p, _ in open_ports:
585 if p in HTTP_PORTS:
586 http_port = p
587 break
588
589 if not http_port:
590 print(c("\n ℹ️ 未发现 HTTP/HTTPS 端口,跳过大包上传测试。", YELLOW))
591 print(c(" 如需强制测试,使用 --http-port <端口>", GRAY))
592 return
593
594 print(c(f"\n ── 阶段3:HTTP 大包上传压测 ──", BOLD))
595 r = http_bulk_stress(
596 host=host, port=http_port,
597 total_gb=args.http_size,
598 chunk_mb=args.chunk_mb,
599 concurrency=args.http_concurrency,
600 timeout=args.http_timeout,
601 )
602 print_http_bulk_report(r)
603
604
605if __name__ == "__main__":
606 main()
607