Python 检测远程 UDP 端口是否打开的方法

Dylan
Dylan
发布时间:2026-10-12| 3人浏览过
原创

本文详解如何使用 Python 结合 socket 与 threading 模块,通过发送 UDP 探测包并监听 ICMP 响应报文,判定远程主机 UDP 端口状态。提供适配 Python 3 的完整可运行代码、调用入口解析及防火墙拦截等实际排错指南。

UDP 端口检测的核心原理

由于 UDP 是无连接协议,无法像 TCP 那样通过三次握手确认端口状态。常用的检测思路是向目标 IP 的指定端口发送一段测试数据,随后监听主机是否返回“ICMP 目标端口不可达(Type 3, Code 3)”错误报文。若收到该报文,说明端口未开启或被过滤;若在设定超时时间内无任何回应,则通常判定为端口开放或服务正在通信中。需要注意的是,现代操作系统与防火墙常默认丢弃此类 ICMP 报错,实际命中率受安全策略影响较大。

环境准备

脚本基于 Python 3 标准库编写,无需安装第三方依赖。因涉及原始套接字(SOCK_RAW)接收 ICMP 底层报文,部分系统可能需要管理员权限或 Root 账户执行。确保目标网络可正常收发 UDP 与 ICMP 协议报文,以便准确触发校验流程。

完整实现代码

以下为重构并适配 Python 3 的探测脚本。核心包含 UDP 发包线程、ICMP 收包校验线程以及结果汇总逻辑,已修复旧版语法废弃项并优化网络超时控制。

import socket
import threading
import time
import struct
import queue

detect_queue = queue.Queue()

def udp_sender(ip, port):
    """向指定目标地址发送 UDP 探测包"""
    try:
        ADDR = (ip, port)
        sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock_udp.sendto(b"test_ping", ADDR)
        sock_udp.close()
    except Exception:
        pass

def icmp_receiver(ip, port):
    """监听 ICMP 响应报文并校验端口状态"""
    icmp_protocol = socket.getprotobyname("icmp")
    try:
        sock_icmp = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_protocol)
    except OSError as e:
        # 捕获无权限创建原始套接字的错误提示
        raise OSError(f"{e} - 注:需以管理员或 Root 身份运行此脚本以获取 ICMP 监听权限。")

    sock_icmp.settimeout(3.0)
    try:
        rec_packet, addr = sock_icmp.recvfrom(1024)
    except socket.timeout:
        detect_queue.put(True)
        return

    # 提取 ICMP 头部类型、代码、校验和、ID、序列号
    icmp_header_data = rec_packet[20:28]
    head_type, code, checksum, packet_id, sequence = struct.unpack("!bbHHh", icmp_header_data)

    # 校验是否为端口不可达错误,且源 IP 匹配
    sock_icmp.close()
    if code == 3 and addr[0] == ip:
        detect_queue.put(False)
    else:
        detect_queue.put(True)

def checker_udp(ip, port):
    """并发启动探测线程并返回最终状态"""
    thread_udp = threading.Thread(target=udp_sender, args=(ip, port))
    thread_icmp = threading.Thread(target=icmp_receiver, args=(ip, port))

    thread_udp.daemon = True
    thread_icmp.daemon = True

    thread_icmp.start()
    time.sleep(0.1)
    thread_udp.start()

    thread_icmp.join()
    thread_udp.join()
    return detect_queue.get_nowait()

if __name__ == "__main__":
    import sys
    target_ip = sys.argv[1]
    target_port = int(sys.argv[2])
    print(checker_udp(target_ip, target_port))

第1步:封装单端口的探测逻辑

目的:隔离 UDP 发包与 ICMP 收包任务,避免阻塞主线程。
入口:调用 checker_udp(ip, port)。
关键动作:初始化 socket 并构造目标地址元组 (ip, port),使用 sendto 注入探测载荷后关闭连接;同时创建 Raw Socket 绑定 ICMP 协议,设置 settimeout(3.0) 等待响应。

第2步:调度并发线程并汇总结果

目的:同步执行发收流程,按规则输出端口状态。
入口:脚本直接运行或作为模块导入调用。
关键动作:将收发函数包装为 threading.Thread 实例,标记为守护线程防止异常中断拖累进程。先启动 ICMP 监听器,短暂休眠后触发 UDP 发送器。调用 join() 等待双方完成,最后通过队列取回布尔值:True 表示可能开放或未响应,False 表示明确关闭。

运行验证与排错指南

终端执行命令格式如下:python scanner.py <目标IP> <端口号>。输出 True 代表目标可能在监听或安全策略阻止了 ICMP 回馈;输出 False 则证明端口处于未开放状态。

  • 权限不足报错:Windows 下通常自动放行,macOS/Linux 若抛出 OSError,需提前执行 sudo 提权。
  • 始终返回 True(误判):企业级防火墙(如 iptables、Windows Defender)默认屏蔽 Type 3 类 ICMP 消息。此时脚本会因收不到反馈而判定为“开放”。建议在测试环境或内网无拦截节点中验证。
  • IPv6 兼容问题:当前逻辑锁定 AF_INET(IPv4)。若需扫描 IPv6 目标,请将套接字族替换为 socket.AF_INET6 并调整地址格式。

简要总结

本方案利用标准网络协议栈的特性,以极低的资源消耗实现了基础 UDP 端口状态探测。对于常规运维排查或学习参考具备直接可用性,但在面对复杂网络拓扑与强安全网关时,建议配合成熟的扫描框架进行交叉验证。以上就是 Python 检测远程 UDP 端口是否打开的详细方法与注意事项,更多关于网络探测与 socket 编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。