做安全测试或运维巡检时,我们常常只知道一个域名或 IP,却不清楚它背后到底开放了哪些服务。逐个手工尝试既费时又容易遗漏,而端口扫描正是解决这一问题的第一步。本文为初学者第一次尝试简易的端口扫描,从零开始用 Python 的 socket 模块实现一个轻量级端口扫描器,从中理解 TCP 连接探测的基本原理。
环境准备:python3.11
import socket
def port_scan(target, port):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((target, port))
if result == 0:
print(f"Port {port} is open")
else:
print(f"Port {port} is closed")
sock.close()
except Exception as e:
print(f"Error scanning {port}: {e}")
for port in [21, 22, 80, 443, 3389]:
port_scan("example.com", port)
【运行结果】
【重要提醒】connect_ex 的返回值:与connect的区别是它不会像 connect 那样抛出异常,而是用整数表示连接结果,返回 0 表示端口开放、连接成功;返回非 0 值(如 111 表示连接被拒绝、110 表示超时)则说明端口关闭或被过滤,因此可以直接用 result == 0 判断端口状态。settimeout 的作用:它为 socket 设置超时时间(单位秒),这里设为 1 秒,意味着连接尝试最多等待 1 秒,超时后 connect_ex 会返回超时错误码而不是一直阻塞,从而避免扫描大量端口时程序长时间卡住,也让扫描速度更可控。
总结
- 扫描原理:利用 socket 发起 TCP 三次握手的第一步,通过 connect_ex 的返回值判断目标端口是否开放,返回 0 即连接成功、端口开放。
- 超时控制:用 settimeout 为每次连接设置等待上限,避免在关闭或被过滤的端口上长时间阻塞,让批量扫描的速度和稳定性都可控。
- 异常处理:用 try/except 包裹单次探测,把域名解析失败、网络不可达等异常隔离在单个端口内,保证循环扫描不会因一次报错而中断。
- 资源释放:每次探测结束后调用 sock.close() 关闭连接,避免大量扫描时耗尽本地文件描述符。
使用提醒
端口扫描属于主动探测行为,请务必仅对自己拥有或已获得明确书面授权的主机进行扫描。未经授权扫描他人服务器,可能违反《网络安全法》等法律法规,也可能触发对方的安全防护与告警机制,带来不必要的法律风险和纠纷。学习阶段建议使用本地环境、自建虚拟机或官方提供的公开测试靶场(如本地搭建的测试服务)来验证代码,把技术用在合规的场景中。



