socket.connect_ex() 是检测 tcp 端口是否被占用最可靠、轻量且跨平台的方式,返回 0 表示端口监听,111 或 61 表示被占,其余多为网络不可达;需设 timeout、用线程池控制并发,并区分错误码避免误判。

用 socket.connect_ex() 检查端口是否被占用
直接尝试连接目标端口是最可靠的方式,比查 netstat 或 lsof 更轻量、跨平台。Python 的 socket.connect_ex() 返回 0 表示连接成功(即端口正在监听),非 0(通常是 111、61、10061 等)表示拒绝或不可达。
注意:这只能检测 TCP 端口;UDP 需用 sendto + 超时判断,但多数运维场景关心的是 TCP 监听状态。
- 必须设置
timeout,否则阻塞几秒甚至更久(尤其对无响应主机) - 不要用
socket.connect()—— 它抛异常而非返回码,捕获异常开销大且不易区分“端口未开”和“主机不可达” - 检查前建议先
socket.gethostbyname()或直接用 IP,避免 DNS 查询拖慢批量检测
批量扫描多个端口时如何避免超时堆积
串行逐个 connect_ex 太慢,尤其扫描几十个端口+多台服务器时。用 threading 最简单,但要注意线程数控制 —— 开太多反而触发系统限制或被防火墙限速。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 推荐固定线程池,比如
concurrent.futures.ThreadPoolExecutor(max_workers=10) - 每个任务设独立
socket.timeout = 2.0,防止某台服务器卡死整个池 - 别用
asyncio.open_connection除非你已在异步框架里;纯脚本里 threading 更直观、调试友好 - 结果用字典或命名元组收集,包含
host、port、is_used、error字段,方便后续过滤
绕过权限限制:普通用户无法检测 1–1023 端口?
Linux/macOS 下,非 root 用户调用 connect_ex 扫描特权端口(如 22、80、443)本身是允许的 —— 这不是“绑定”,只是发起连接尝试。真正受限的是 bind() 或读取 /proc/net/tcp。
- 只要目标服务在监听且防火墙放行,普通用户也能得到准确的 “端口开放” 结果
- 如果返回
13(Permission denied),说明本地策略(如 SELinux 或某些容器环境)阻止了 socket 创建,不是端口问题 - 若需知道哪个进程占用了 80 端口,那确实需要
sudo lsof -i :80,但自动脚本通常不依赖这个信息
输出结果时怎么避免误判“端口空闲”
常见错误是把连接失败一律当“端口空闲”。实际上,connect_ex 返回非 0 值有多种含义:113(No route to host)、111(Connection refused)、10060(Windows timeout)等。只有 111 和 61 明确表示“服务在监听但拒绝连接”(即端口被占),其它多数情况是网络不通或服务未启。
- 脚本中应区分错误码:
if result == 0: occupied = True;elif result == 111 or result == 61: occupied = True;其余视为不可达 - 加个
--verbose参数输出原始错误码,排查时省去重跑成本 - 对关键端口(如数据库 3306、Redis 6379)建议额外加一次
telnet host port人工验证,避免中间设备干扰
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










