Socket-TCP 简易端口开放检测工具

使用C++实现的TCP端口开放检测工具,支持单个端口、多个端口和端口范围的检测,具有简洁的命令行界面和详细的扫描结果输出。

源代码

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <conio.h>
#include <thread>
#include <mutex>
#include <atomic>
#include <queue>
#include <Windows.h>
#include <iphlpapi.h>
#include <algorithm>
#include <iomanip>
#include <map>
#include <set>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")

// 最大端口号
const int MAX_PORT = 65535;
// 默认超时时间(毫秒)
const int DEFAULT_TIMEOUT = 2000;
// 最大线程数
const int MAX_THREADS = 100;

// 端口扫描结果结构体
struct PortResult {
    int port;
    bool isOpen;
    std::string serviceName;
    std::string banner;
};

// 全局变量
std::mutex g_mutex;
std::atomic<int> g_activeThreads(0);
std::atomic<int> g_scannedPorts(0);
std::atomic<int> g_openPorts(0);
std::queue<PortResult> g_results;
std::map<int, std::string> g_commonPorts = {
    {7, "Echo"},
    {20, "FTP-DATA"},
    {21, "FTP"},
    {22, "SSH"},
    {23, "Telnet"},
    {25, "SMTP"},
    {53, "DNS"},
    {67, "DHCP"},
    {68, "DHCP"},
    {69, "TFTP"},
    {80, "HTTP"},
    {110, "POP3"},
    {119, "NNTP"},
    {123, "NTP"},
    {137, "NetBIOS"},
    {138, "NetBIOS"},
    {139, "NetBIOS"},
    {143, "IMAP"},
    {161, "SNMP"},
    {162, "SNMP"},
    {389, "LDAP"},
    {443, "HTTPS"},
    {445, "SMB"},
    {465, "SMTPS"},
    {514, "Syslog"},
    {587, "SMTP"},
    {636, "LDAPS"},
    {993, "IMAPS"},
    {995, "POP3S"},
    {1080, "SOCKS"},
    {1433, "MSSQL"},
    {1521, "Oracle"},
    {1723, "PPTP"},
    {3306, "MySQL"},
    {3389, "RDP"},
    {5432, "PostgreSQL"},
    {5900, "VNC"},
    {8080, "HTTP-Proxy"},
    {8443, "HTTPS-Alt"}
};

// 获取服务名称
std::string GetServiceName(int port, const std::string& protocol = "tcp") {
    std::string serviceName = "Unknown";
    
    // 检查常见端口
    auto it = g_commonPorts.find(port);
    if (it != g_commonPorts.end()) {
        serviceName = it->second;
    }
    
    // 尝试通过系统API获取服务名称
    struct servent* serv = getservbyport(htons(port), protocol.c_str());
    if (serv != nullptr) {
        serviceName = serv->s_name;
    }
    
    return serviceName;
}

// 尝试获取Banner
std::string GetBanner(SOCKET sock) {
    char buffer[1024] = {0};
    int bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (bytesReceived > 0) {
        return std::string(buffer, bytesReceived);
    }
    return "";
}

// 扫描单个端口
void ScanPort(const std::string& target, int port, int timeout) {
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        return;
    }

    // 设置超时
    DWORD timeoutVal = timeout;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));

    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, target.c_str(), &server.sin_addr);

    // 尝试连接
    int result = connect(sock, (sockaddr*)&server, sizeof(server));
    bool isOpen = (result != SOCKET_ERROR);

    PortResult res;
    res.port = port;
    res.isOpen = isOpen;
    res.serviceName = isOpen ? GetServiceName(port) : "";
    res.banner = "";

    if (isOpen) {
        // 尝试获取Banner
        res.banner = GetBanner(sock);
        g_openPorts++;
    }

    // 保存结果
    {
        std::lock_guard<std::mutex> lock(g_mutex);
        g_results.push(res);
    }

    closesocket(sock);
    g_scannedPorts++;
    g_activeThreads--;
}

// 工作线程函数
void WorkerThread(const std::string& target, const std::vector<int>& ports, int timeout) {
    while (!ports.empty()) {
        int port = 0;
        {
            // 从队列中获取端口
            std::lock_guard<std::mutex> lock(g_mutex);
            if (ports.empty()) break;
            port = ports.back();
            ports.pop_back();
        }
        
        g_activeThreads++;
        ScanPort(target, port, timeout);
    }
}

// 解析端口字符串
std::vector<int> ParsePorts(const std::string& portStr) {
    std::vector<int> ports;
    std::istringstream ss(portStr);
    std::string token;
    
    while (std::getline(ss, token, ',')) {
        // 检查是否是端口范围
        size_t dashPos = token.find('-');
        if (dashPos != std::string::npos) {
            int start = std::stoi(token.substr(0, dashPos));
            int end = std::stoi(token.substr(dashPos + 1));
            for (int p = start; p <= end; p++) {
                if (p >= 1 && p <= MAX_PORT) {
                    ports.push_back(p);
                }
            }
        } else {
            // 单个端口
            int port = std::stoi(token);
            if (port >= 1 && port <= MAX_PORT) {
                ports.push_back(port);
            }
        }
    }
    
    // 去重并排序
    std::sort(ports.begin(), ports.end());
    ports.erase(std::unique(ports.begin(), ports.end()), ports.end());
    
    return ports;
}

// 显示扫描进度
void DisplayProgress(int totalPorts) {
    std::cout << "\r扫描进度: " << g_scannedPorts << "/" << totalPorts 
              << " (" << std::fixed << std::setprecision(1) 
              << (static_cast<float>(g_scannedPorts) / totalPorts * 100) << "%)"
              << " | 开放端口: " << g_openPorts
              << " | 活动线程: " << g_activeThreads << std::flush;
}

// 显示扫描结果
void DisplayResults() {
    std::cout << "\n\n扫描结果:\n";
    std::cout << "========================================\n";
    std::cout << std::left << std::setw(8) << "端口" 
              << std::setw(20) << "状态" 
              << std::setw(20) << "服务" 
              << "Banner\n";
    std::cout << "----------------------------------------\n";
    
    // 收集所有结果
    std::vector<PortResult> allResults;
    {
        std::lock_guard<std::mutex> lock(g_mutex);
        while (!g_results.empty()) {
            allResults.push_back(g_results.front());
            g_results.pop();
        }
    }
    
    // 按端口号排序
    std::sort(allResults.begin(), allResults.end(), 
        const PortResult& a, const PortResult& b { return a.port < b.port; });
    
    // 显示结果
    for (const auto& res : allResults) {
        std::cout << std::left << std::setw(8) << res.port
                  << std::setw(20) << (res.isOpen ? "开放" : "关闭")
                  << std::setw(20) << res.serviceName;
        
        if (res.isOpen && !res.banner.empty()) {
            // 截断过长的banner
            std::string displayBanner = res.banner;
            if (displayBanner.length() > 50) {
                displayBanner = displayBanner.substr(0, 47) + "...";
            }
            std::cout << displayBanner;
        }
        std::cout << "\n";
    }
    
    std::cout << "========================================\n";
    std::cout << "扫描完成! 共扫描 " << allResults.size() << " 个端口, 发现 " 
              << g_openPorts << " 个开放端口\n";
}

// 解析IP地址
std::string ResolveHostname(const std::string& hostname) {
    struct addrinfo hints, *res;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    
    if (getaddrinfo(hostname.c_str(), NULL, &hints, &res) != 0) {
        return "";
    }
    
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &((struct sockaddr_in*)res->ai_addr)->sin_addr, ip, sizeof(ip));
    freeaddrinfo(res);
    
    return std::string(ip);
}

// 检查IP是否有效
bool IsValidIP(const std::string& ip) {
    struct sockaddr_in sa;
    return inet_pton(AF_INET, ip.c_str(), &sa.sin_addr) == 1;
}

// 显示帮助信息
void ShowHelp() {
    std::cout << "TCP端口扫描工具 v1.0\n";
    std::cout << "用法: portscanner [选项] <目标> <端口>\n\n";
    std::cout << "选项:\n";
    std::cout << "  -t <线程数>   设置扫描线程数 (默认: 10)\n";
    std::cout << "  -o <超时>     设置连接超时时间(毫秒) (默认: 2000)\n";
    std::cout << "  -h            显示帮助信息\n\n";
    std::cout << "端口格式:\n";
    std::cout << "  单个端口: 80\n";
    std::cout << "  多个端口: 80,443,8080\n";
    std::cout << "  端口范围: 1-1000\n";
    std::cout << "  组合: 21,22,80,443,8000-8100\n\n";
    std::cout << "示例:\n";
    std::cout << "  portscanner 192.168.1.1 80\n";
    std::cout << "  portscanner example.com 20-100\n";
    std::cout << "  portscanner -t 50 -o 1000 10.0.0.1 1-1024\n";
}

// 主函数
int main(int argc, char* argv[]) {
    // 默认参数
    int threadCount = 10;
    int timeout = DEFAULT_TIMEOUT;
    std::string target;
    std::string portStr;
    
    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "-t" && i + 1 < argc) {
            threadCount = std::stoi(argv[++i]);
            if (threadCount < 1) threadCount = 1;
            if (threadCount > MAX_THREADS) threadCount = MAX_THREADS;
        } else if (arg == "-o" && i + 1 < argc) {
            timeout = std::stoi(argv[++i]);
            if (timeout < 100) timeout = 100;
            if (timeout > 10000) timeout = 10000;
        } else if (arg == "-h") {
            ShowHelp();
            return 0;
        } else if (target.empty()) {
            target = arg;
        } else if (portStr.empty()) {
            portStr = arg;
        }
    }
    
    // 检查必要参数
    if (target.empty() || portStr.empty()) {
        std::cout << "错误: 缺少目标或端口参数!\n";
        ShowHelp();
        return 1;
    }
    
    // 初始化Winsock
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "WSAStartup失败!\n";
        return 1;
    }
    
    // 解析目标地址
    std::string ip = target;
    if (!IsValidIP(target)) {
        std::cout << "正在解析域名: " << target << "...\n";
        ip = ResolveHostname(target);
        if (ip.empty()) {
            std::cerr << "无法解析域名: " << target << "\n";
            WSACleanup();
            return 1;
        }
        std::cout << "解析结果: " << target << " -> " << ip << "\n";
    }
    
    // 解析端口
    std::vector<int> ports = ParsePorts(portStr);
    if (ports.empty()) {
        std::cerr << "错误: 无效的端口格式!\n";
        WSACleanup();
        return 1;
    }
    
    // 限制扫描端口数量
    if (ports.size() > 10000) {
        std::cout << "警告: 扫描端口数量过多 (" << ports.size() << "), 建议减少范围\n";
        std::cout << "是否继续? (y/n): ";
        char choice = _getch();
        std::cout << "\n";
        if (choice != 'y' && choice != 'Y') {
            WSACleanup();
            return 0;
        }
    }
    
    std::cout << "开始扫描 " << ip << " 的 " << ports.size() << " 个端口...\n";
    std::cout << "线程数: " << threadCount << ", 超时: " << timeout << "ms\n";
    
    // 创建线程池
    std::vector<std::thread> threads;
    std::vector<std::vector<int>> threadPorts(threadCount);
    
    // 分配端口给线程
    for (size_t i = 0; i < ports.size(); i++) {
        threadPorts[i % threadCount].push_back(ports[i]);
    }
    
    // 启动线程
    auto startTime = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < threadCount; i++) {
        threads.emplace_back(WorkerThread, ip, std::ref(threadPorts[i]), timeout);
    }
    
    // 显示进度
    while (g_scannedPorts < ports.size()) {
        DisplayProgress(ports.size());
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    // 等待所有线程完成
    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }
    
    auto endTime = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
    
    // 显示结果
    DisplayResults();
    std::cout << "扫描耗时: " << duration.count() << "ms\n";
    
    // 清理
    WSACleanup();
    return 0;
}

功能特点

  1. 灵活的端口扫描

    • 支持单个端口扫描(如80)
    • 支持多个端口扫描(如80,443,8080)
    • 支持端口范围扫描(如1-1000)
    • 支持混合格式(如21,22,80,443,8000-8100)
  2. 多线程扫描

    • 可配置的线程数量(默认10线程)
    • 线程池管理,高效利用系统资源
    • 实时显示扫描进度
  3. 详细的结果展示

    • 端口开放状态(开放/关闭)
    • 服务名称识别(基于常见端口和服务数据库)
    • Banner抓取(尝试获取服务标识)
    • 扫描统计信息(耗时、扫描端口数、开放端口数)
  4. 网络诊断功能

    • 域名解析(自动将域名转换为IP地址)
    • 自定义连接超时时间(默认2000ms)
    • 详细的错误处理和帮助信息

参考代码 socket-tcp简易端口开放检测工具 www.youwenfan.com/contentcst/122324.html

使用方法

命令行参数

复制代码
portscanner [选项] <目标> <端口>

选项:
  -t <线程数>   设置扫描线程数 (默认: 10)
  -o <超时>     设置连接超时时间(毫秒) (默认: 2000)
  -h            显示帮助信息

端口格式:
  单个端口: 80
  多个端口: 80,443,8080
  端口范围: 1-1000
  组合: 21,22,80,443,8000-8100

使用示例

  1. 扫描单个端口:

    复制代码
    portscanner 192.168.1.1 80
  2. 扫描多个端口:

    复制代码
    portscanner example.com 80,443,8080
  3. 扫描端口范围:

    复制代码
    portscanner 10.0.0.1 1-1024
  4. 自定义线程数和超时时间:

    复制代码
    portscanner -t 50 -o 1000 192.168.1.1 1-65535

技术实现细节

1. 端口扫描核心逻辑

cpp 复制代码
void ScanPort(const std::string& target, int port, int timeout) {
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    // 设置超时
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, ...);
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, ...);
    
    // 创建套接字地址结构
    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, target.c_str(), &server.sin_addr);
    
    // 尝试连接
    int result = connect(sock, (sockaddr*)&server, sizeof(server));
    bool isOpen = (result != SOCKET_ERROR);
    
    // 获取服务名称和Banner
    if (isOpen) {
        serviceName = GetServiceName(port);
        banner = GetBanner(sock);
    }
    
    // 保存结果
    g_results.push({port, isOpen, serviceName, banner});
    closesocket(sock);
}

2. 多线程管理

cpp 复制代码
void WorkerThread(const std::string& target, const std::vector<int>& ports, int timeout) {
    while (!ports.empty()) {
        int port = ports.back();
        ports.pop_back();
        ScanPort(target, port, timeout);
    }
}

// 主函数中创建线程池
for (int i = 0; i < threadCount; i++) {
    threads.emplace_back(WorkerThread, ip, std::ref(threadPorts[i]), timeout);
}

3. 服务识别与Banner抓取

cpp 复制代码
std::string GetServiceName(int port, const std::string& protocol) {
    // 检查常见端口映射
    auto it = g_commonPorts.find(port);
    if (it != g_commonPorts.end()) {
        return it->second;
    }
    
    // 使用系统API获取服务名称
    struct servent* serv = getservbyport(htons(port), protocol.c_str());
    if (serv != nullptr) {
        return serv->s_name;
    }
    
    return "Unknown";
}

std::string GetBanner(SOCKET sock) {
    char buffer[1024] = {0};
    int bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (bytesReceived > 0) {
        return std::string(buffer, bytesReceived);
    }
    return "";
}

4. 进度显示与结果汇总

cpp 复制代码
void DisplayProgress(int totalPorts) {
    std::cout << "\r扫描进度: " << g_scannedPorts << "/" << totalPorts 
              << " (" << (static_cast<float>(g_scannedPorts)/totalPorts*100) << "%)"
              << " | 开放端口: " << g_openPorts
              << " | 活动线程: " << g_activeThreads << std::flush;
}

void DisplayResults() {
    // 收集所有结果并按端口排序
    std::vector<PortResult> allResults;
    // ... 收集结果 ...
    
    // 显示表格形式的结果
    std::cout << std::left << std::setw(8) << "端口" 
              << std::setw(20) << "状态" 
              << std::setw(20) << "服务" 
              << "Banner\n";
    // ... 显示每个端口的结果 ...
}

编译与使用

Windows 编译

  1. 使用Visual Studio创建控制台应用程序项目
  2. 复制源代码到项目中
  3. 配置项目属性:
    • 链接器 → 输入 → 附加依赖项:ws2_32.lib;iphlpapi.lib
    • C/C++ → 预处理器 → 预处理器定义:_CRT_SECURE_NO_WARNINGS
  4. 编译并运行

Linux 编译(使用Wine兼容层)

bash 复制代码
# 安装必要的库
sudo apt-get install g++ mingw-w64 wine

# 编译Windows可执行文件
i686-w64-mingw32-g++ -o portscanner.exe portscanner.cpp -lws2_32 -liphlpapi -static

# 运行(需要Wine)
wine portscanner.exe 127.0.0.1 1-1024

Linux 原生版本

cpp 复制代码
// 替换Windows特有的头文件和函数
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket close
#endif

// 初始化网络库
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#else
// 不需要特殊初始化
#endif

应用场景

  1. 网络安全评估

    • 检查系统开放的端口和服务
    • 识别潜在的安全风险
    • 验证防火墙配置
  2. 网络故障排查

    • 确认服务是否在预期端口运行
    • 诊断网络连接问题
    • 验证网络设备配置
  3. 系统管理员日常维护

    • 定期检查服务器端口状态
    • 监控服务可用性
    • 文档化网络服务
  4. 渗透测试

    • 侦察目标系统开放的服务
    • 识别易受攻击的服务
    • 规划后续攻击向量
相关推荐
不会写DN2 小时前
TCP 长连接服务:登录注册认证体系实战指南
服务器·网络·网络协议·tcp/ip·计算机网络·面试
壹方秘境2 小时前
Wireshark 太难?ChatTCP 把 TCP 数据包变成“微信对话”
网络·测试工具·wireshark
一只小鱼儿吖2 小时前
基于OpenClaw的代理IP池自动化监控方案
网络协议·tcp/ip·自动化
蕤葳-3 小时前
理性分析:如何利用考证作为抓手,构建系统化知识体系与职业规划?
人工智能·网络协议·https
gamers3 小时前
客户终端的DNS被劫持,网络能访问,但浏览器域名访问不到
网络·dns劫持
AI精钢3 小时前
Claude Opus 4.7 是一次失败的升级吗?一次基于用户反馈的技术复盘
网络·人工智能·ai·大模型·llm·claude·技术评论
liulian09163 小时前
Flutter 三方库 connectivity_plus 的鸿蒙化适配与网络状态管理实战
网络·flutter·华为·学习方法·harmonyos
通信小呆呆4 小时前
各具神通——Vivado中不同系列的IP核差异详解
网络协议·tcp/ip·fpga开发
不会写DN4 小时前
通过eino-ext如何正常indexer RAG?
网络·面试·go