Jteckz/tcp-port-scanner-
GitHub: Jteckz/tcp-port-scanner-
一个基于 C++17 的多线程 TCP connect 扫描器,用于教学演示端口扫描原理并支持服务 Banner 抓取。
Stars: 0 | Forks: 0
# TCP 端口扫描器




一个使用现代 C++ (C++17) 编写的多线程 TCP connect-scanner,旨在
理解像 Nmap 这样的工具在底层的实际工作原理:在带有超时的情况下并行
向一系列端口发起原始 TCP 连接,
并从响应端抓取服务 banner。
```
Scanning scanme.nmap.org ports 1-1000 with 100 threads...
Resolved to 45.33.32.156
PORT STATE SERVICE BANNER
22 open ssh SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13
80 open http HTTP/1.0 200 OK Server: Apache/2.4.7 ...
Scanned 1000 ports in 842 ms. 2 open.
```
## 目录
- [功能介绍](#what-it-does)
- [构建](#build)
- [用法](#usage)
- [工作原理](#how-it-works-design-notes)
- [项目结构](#project-structure)
- [文档](#documentation)
- [可能的扩展](#possible-extensions)
- [局限性](#limitations)
- [法律与道德使用](#legal--ethical-use)
- [许可证](#license)
## 功能介绍
- **Connect scanning** — 对每个端口发起真实的 TCP 三次握手
(`socket()` + `connect()`),这与 `nmap -sT` 背后使用的技术相同。
- **带超时的非阻塞 connects** — 每个 socket 在 `connect()` 之前被设置为非阻塞,
然后使用 `select()` 等待连接完成或超时,这样单个缓慢/被过滤的端口就不会停滞整个扫描过程。
- **多线程** — 一组工作线程池从共享的 atomic counter 中获取端口并进行并发扫描,
与简单的顺序循环相比,大幅缩短了扫描时间。
- **Banner grabbing** — 成功建立连接后,扫描器会尝试读取服务发送的任何信息
(SSH、FTP、SMTP 会立即进行自我公告),而对于 HTTP 系列端口,它会首先发送一个 `HEAD / HTTP/1.0`
探测请求,因为这些服务在被访问之前会保持沉默。
- **服务名称猜测** — 一个小型的查找表将知名端口
(22、80、443、3306 等)映射到对应的服务名称,就像一个简化的 `/etc/services`。
## 构建
需要 C++17 编译器和 CMake 3.10+ 版本。
```
git clone https://github.com//tcp-port-scanner.git
cd tcp-port-scanner
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
```
这将生成 `build/scanner`。
## 用法
```
./scanner [start_port] [end_port] [options]
```
| 选项 | 描述 | 默认值 |
|---|---|---|
| `-t ` | 并发工作线程数 | 100 |
| `-T ` | 每个端口的连接超时时间(毫秒) | 500 |
| `--no-banner` | 跳过 banner 抓取以加快扫描速度 | 关闭 |
| `-h`, `--help` | 显示用法 | |
### 示例
```
# 扫描主机的 well-known ports
./scanner 192.168.1.10 1 1000
# Full port range,更多线程,更严格的 timeout
./scanner scanme.nmap.org 1 65535 -t 200 -T 300
# 快速扫描,无 banner grabbing
./scanner 10.0.0.5 1 1024 --no-banner
```
## 工作原理(设计笔记)
- **`PortScanner`**(`include/scanner.hpp`,`src/scanner.cpp`)拥有扫描
配置和结果。`run()` 通过 `getaddrinfo` 解析一次目标主机,
然后启动 `numThreads` 个工作线程,每个线程运行
`workerLoop()`。
- 每个工作线程不断从一个单一的
`std::atomic` 计数器(`nextPort_`)中认领下一个端口索引,而不是使用受锁保护的
队列 — 对于简单的“获取下一个整数”任务来说,这种同步方式开销更低。
- **超时处理**:阻塞式的 `connect()` 在被过滤或受防火墙保护的端口上可能会挂起很长时间。
将 socket 设置为非阻塞,并让 `connect()` 与 `select()` 的超时进行赛跑,意味着单个无响应的端口
最多只会消耗 `connectTimeoutMs` 的时间,而不是操作系统的默认 TCP 超时
(通常为 60 秒以上)。
- **线程安全**:只有共享的结果向量需要 mutex
(`resultsMutex_`);每个工作线程的 socket、缓冲区和局部变量对该线程都是私有的。
## 项目结构
```
tcp-port-scanner/
├── CMakeLists.txt
├── README.md
├── AI_COLLABORATION.md # how AI was used in building this, and what I verified myself
├── include/
│ └── scanner.hpp
└── src/
├── scanner.cpp
└── main.cpp
```
## 文档
除了本 README 之外,此代码库还包含两份说明文档:
- **[AI_COLLABORATION.md](AI_COLLABORATION.md)** — 如实记录了在开发过程中是如何使用 AI 的,
哪些是我自己指导和验证的,以及真正的调试工作是在哪里进行的。
- 一份完整的**从零开始的构建指南**(PDF),涵盖了网络基础、
每一个设计决策,以及对 socket/多线程代码的逐行解释 —
它是作为一份教学文档编写的,而不仅仅是 API 文档。
## 可能的扩展
- IPv6 支持(为简单起见,目前仅支持 IPv4)
- SYN 扫描(使用 raw sockets,需要 root 权限)以取代完整的 connect 扫描
- UDP 扫描
- CIDR 范围 / 多主机输入
- 用于脚本化的 JSON 输出
## 局限性
这是一个 **connect scanner**,而不是 SYN/隐身扫描器 — 它会完成
完整的 TCP 握手,因此它会出现在目标日志中,并且在面对极大量端口时,扫描速度比 raw-socket 扫描慢。它被定位为一个学习项目,而不是 Nmap 的替代品。
## 法律与道德使用
仅对你拥有、控制或获得明确测试授权的主机运行此程序 —
例如
[scanme.nmap.org](https://nmap.org/book/legal-issues.html),这是 Nmap
项目专门维护的一个合法的公共扫描目标。未经授权扫描
系统可能会违反计算机滥用法,即使
没有造成任何损害。
## 许可证
MIT — 参见 [LICENSE](LICENSE)(如果你
还没有添加,可以通过 GitHub 的“Add file”流程添加一个,或者在使用 `gh repo create` 时附带 `--license MIT`)。
标签:Bash脚本, C++, DNS查询工具, TCP协议, 插件系统, 数据擦除, 端口扫描器, 网络安全, 隐私保护