Python 检测远程 UDP 端口是否打开的方法
本文详解如何使用 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 编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









