You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python socket端口扫描连接耗时极长问题排查与解决

基础Python端口扫描脚本首端口卡顿、扫描速度慢问题排查与修复

问题复现

我编写了一个基础端口扫描脚本,代码与GeeksforGeeks站点提供的示例高度相似,代码如下:

#!/bin/python3

import socket
import sys
from datetime import datetime

# 定义扫描目标
if len(sys.argv) == 2:
    target = socket.gethostbyname(sys.argv[1]) # 将主机名解析为IPv4地址
else:
    print("Invalid amount of arguments")
    print("Syntax: python3 scanner.py <ip>")
    sys.exit()

# 打印扫描横幅
print("-" * 50)
print("Scanning target " + target)
print("Time started: " + str(datetime.now()))
print("-" * 50)

try:
    for port in range(50,85):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        socket.setdefaulttimeout(1)
        print("Time starting port {}: ".format(port) + str(datetime.now()))
        result = s.connect_ex((target, port)) # 返回错误标识
        if (result == 0):
            print("Port {} is open".format(port))

        s.close()

except KeyboardInterrupt:
    print("Exiting program")
    sys.exit()
except socket.gaierror:
    print("Hostname could not be resolved")
    sys.exit()
except socket.error:
    print("Couldn't connect to server.")
    sys.exit()

脚本运行输出如下:

--------------------------------------------------
Scanning target 142.251.32.110
Time started: 2022-06-11 12:33:43.256562
--------------------------------------------------
Time starting port 50: 2022-06-11 12:33:43.256649
Time starting port 51: 2022-06-11 12:35:53.061330
Time starting port 52: 2022-06-11 12:35:54.063653
Time starting port 53: 2022-06-11 12:35:55.065565
Time starting port 54: 2022-06-11 12:35:56.067881
Time starting port 55: 2022-06-11 12:35:57.084706
Time starting port 56: 2022-06-11 12:35:58.089606
Time starting port 57: 2022-06-11 12:35:59.090590
Time starting port 58: 2022-06-11 12:36:00.485674
Time starting port 59: 2022-06-11 12:36:02.077196
Time starting port 60: 2022-06-11 12:36:03.078435
Time starting port 61: 2022-06-11 12:36:04.147746
Time starting port 62: 2022-06-11 12:36:05.148526
Time starting port 63: 2022-06-11 12:36:06.159334
Time starting port 64: 2022-06-11 12:36:07.161831
Time starting port 65: 2022-06-11 12:36:08.229387
Time starting port 66: 2022-06-11 12:36:09.340348
Time starting port 67: 2022-06-11 12:36:10.427094
Time starting port 68: 2022-06-11 12:36:11.434983
Time starting port 69: 2022-06-11 12:36:12.466602
Time starting port 70: 2022-06-11 12:36:13.879363
Time starting port 71: 2022-06-11 12:36:14.948457
Time starting port 72: 2022-06-11 12:36:16.176371
Time starting port 73: 2022-06-11 12:36:17.211928
Time starting port 74: 2022-06-11 12:36:18.319003
Time starting port 75: 2022-06-11 12:36:19.321525
Time starting port 76: 2022-06-11 12:36:20.323844
Time starting port 77: 2022-06-11 12:36:21.325633
Time starting port 78: 2022-06-11 12:36:22.338753
Time starting port 79: 2022-06-11 12:36:23.340196
Time starting port 80: 2022-06-11 12:36:24.343861
Port 80 is open
Time starting port 81: 2022-06-11 12:36:24.365744
Time starting port 82: 2022-06-11 12:36:25.372192
Time starting port 83: 2022-06-11 12:36:26.382037
Time starting port 84: 2022-06-11 12:36:27.387056

注:通常我会扫描自身局域网内的主机,为避免额外变量影响测试结果,本次选择扫描谷歌的服务器,在自有局域网内测试也存在完全相同的问题。

从输出可见,第一个端口的连接耗时长达2分钟,其余每个端口连接耗时约1秒,但根据教程视频中的演示,扫描50-85端口范围几乎可以瞬时完成。
运行环境为VirtualBox配置的NAT网络模式下的Kali虚拟机,跟随伦理黑客教学视频操作时遇到了这个问题。

已排查信息

  • 已排除操作系统问题:在局域网内其他多台计算机上测试该脚本均存在相同问题,若代码逻辑无错则大概率为路由器相关问题,当前网络设备大多保持默认配置,使用的宽带运营商为FibrOp。
  • 临时规避方案(效果仍不理想):将原代码中创建socket、设置超时的两行调换顺序,同时缩短超时时长,修改后代码片段如下:
socket.setdefaulttimeout(0.01)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

调换顺序后超时规则可对首个连接生效,缩短超时时长可避免每个端口等待1秒,用户可根据连接目标的网络情况进一步调小超时值。但该方案下的扫描速度仍远慢于教程演示效果,因此不作为最终解决方案。

  • 速度差异原因推测:测试发现扫描127.0.0.1/localhost时的扫描速度与教程演示完全一致。因此推测教程演示端口扫描时,是通过有线以太网直连被扫描的路由器设备,WiFi环境下同步端口扫描无法达到该速度。

问题根因与修复方案

根因1:代码逻辑错误导致首个端口超时不生效

原代码中先创建socket对象,再调用socket.setdefaulttimeout(1),而默认超时配置仅对配置创建后新生成的socket对象生效,首个socket创建时未加载超时规则,因此会使用系统默认的TCP连接超时(通常为120-240秒),这就是第一个端口卡顿2分钟的直接原因。

根因2:串行扫描模式本身效率极低

原代码采用单线程串行扫描逻辑,每个端口必须等待上一个端口连接完成(要么连接成功、要么触发超时)才会开始下一个端口的探测,哪怕把超时设为1秒,扫描35个端口最少也需要35秒,自然达不到瞬时完成的效果。教程里的快速扫描本质是用了并发扫描逻辑,同时发起多个端口的连接请求。

最终修复代码

将超时设置移到循环外、socket创建逻辑之前,同时引入线程池实现并发扫描,调整后扫描同网段端口可达到毫秒级完成的效果:

#!/bin/python3
import socket
import sys
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

# 定义单端口扫描逻辑
def scan_port(target, port):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(0.5)
        result = s.connect_ex((target, port))
        s.close()
        if result == 0:
            return port
        return None
    except:
        return None

if len(sys.argv) == 2:
    target = socket.gethostbyname(sys.argv[1])
else:
    print("Invalid amount of arguments")
    print("Syntax: python3 scanner.py <ip>")
    sys.exit()

print("-" * 50)
print("Scanning target " + target)
print("Time started: " + str(datetime.now()))
print("-" * 50)

open_ports = []
try:
    # 用30个线程并发扫描,可根据网络情况调整线程数
    with ThreadPoolExecutor(max_workers=30) as executor:
        futures = [executor.submit(scan_port, target, port) for port in range(50,85)]
        for future in as_completed(futures):
            res = future.result()
            if res:
                open_ports.append(res)
                print(f"Port {res} is open")
except KeyboardInterrupt:
    print("\nExiting program")
    sys.exit()
except socket.gaierror:
    print("Hostname could not be resolved")
    sys.exit()
except socket.error:
    print("Couldn't connect to server.")
    sys.exit()

print(f"Scan finished. Open ports: {sorted(open_ports)}")

额外网络层面优化

  • 虚拟机网络优先选择桥接模式,NAT模式会额外经过一层虚拟网络地址转换,会增加连接延迟;
  • 扫描公网目标时速度受运营商网络QoS、目标主机防火墙策略影响,不可能达到本地回环、局域网有线直连的速度,属于正常现象;
  • 部分运营商默认会对高频端口探测做流量限制,公网扫描时可适当降低并发线程数、调高超时阈值,避免被限流。

内容的提问来源于stack exchange,提问作者Logan MacDougall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 18:01:03