• 文章
  • 简单的端口扫描器
发布
2013 年 4 月 16 日 (最后更新: 2013 年 4 月 17 日)

简单的端口扫描器

评分: 4.3/5 (199 票)
*****

先决条件


简介


端口扫描器是一个程序,它通过逐个端口尝试连接服务器来探测服务器的开放端口。该程序通常会报告哪些端口是开放的,哪些是关闭的。更复杂的端口扫描器,如 Nmap,还可以探测其他信息。端口扫描通常由系统管理员执行以验证网络安全,或由攻击者寻找开放端口以破坏服务器安全。开放端口可能构成安全漏洞,因为它们允许远程计算机连接。意外开放的端口可能表明恶意软件正在监听指令。

我的第一个端口扫描器


和之前一样,扫描端口就像尝试连接到地址和端口一样简单。如果连接尝试成功,则端口必须是开放的。否则,则假定端口是关闭的。下面是一个使用 SFML 网络模块 (文档) 来检查端口是否开放的示例函数。

1
2
3
4
5
6
7
bool port_is_open(const std::string& address, int port)
{
    sf::TcpSocket socket;
    bool open = (socket.connect(sf::IpAddress(address), port) == sf::Socket::Done);
    socket.disconnect();
    return open;
}


细分
  1. 首先,我们创建一个 sf::TcpSocket 实例 (文档),它允许我们连接到远程套接字。
  2. 然后,我们连接套接字。通过调用 sf::IpAddress 的构造函数,我们将字符串 'address' 转换为 sf::IpAddress (文档) 实例。如果省略了显式的构造函数调用,编译器也会隐式执行。在尝试连接后,我们通过将 sf::TcpSocket::connect 函数 (文档) 的返回值与枚举值 sf::Socket::Done (文档) 进行比较来检查连接是否成功。如果两者相等,则表示连接成功,端口是开放的。在这种情况下,变量 'open' 被设置为 true。
  3. 接下来,我们使用 sf::TcpSocket::disconnect 函数 (文档) 断开套接字连接。如果我们省略了显式调用,析构函数会自动完成此操作。
  4. 最后,我们将 'open' 的值返回给调用函数。
这实际上是我们唯一需要编写的网络代码。我们也可以将其简化为一行。
1
2
3
4
static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}

这里我们创建了一个新的 sf::TcpSocket,连接到地址和端口,然后根据连接是否成功返回 true 或 false。我们去掉了不必要的显式 sf::IpAddress 构造函数调用以及对 sf::TcpSocket::disconnect() 的调用。我们可以像这样在程序中使用该函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <SFML/Network.hpp>
#include <string>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}

int main()
{
    std::cout << "Port 80 : ";
    if (port_is_open("localhost", 80))
        std::cout << "OPEN" << std::endl;
    else
        std::cout << "CLOSED" << std::endl;
    return 0;
}
Port 80 : OPEN

尝试编译并运行此程序。它将测试您计算机上的 80 端口是否开放。请注意,“localhost”表示本地计算机;您也可以使用 IP 地址 127.0.0.1 或 ::1(IPv6 版本,尽管 SFML 尚不支持 IPv6)来实现相同目的。您可以将“localhost”更改为另一个网站的 IP 地址或 Web 地址(省略“http://”和任何路径信息),但请小心 - **未经许可扫描网站可能会在某些国家/地区给您带来麻烦**,因为它可能被视为黑客行为。幸运的是,端口扫描器 Nmap 的网站有一个专门用于测试端口扫描器的页面。尝试将“localhost”更改为“scanme.nmap.org”。只是不要扫描太多次(页面上说“每天几次”)。

改进的端口扫描器


既然我们已经成功开发了一个可以扫描地址上端口的程序,我们就可以修改我们的程序,让用户指定要扫描的端口和地址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <SFML/Network.hpp>
#include <string>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}


int main()
{
    std::string address;
    int port;
    // Get the address.
    std::cout << "Address: " << std::flush;
    std::getline(std::cin, address);
    // Get the port.
    std::cout << "Port: " << std::flush;
    std::cin >> port;
    // Scan!
    std::cout << "Scanning " << address << "...\n" << "Port " << port << " : ";
    if (port_is_open(address, port))
        std::cout << "OPEN" << std::endl;
    else
        std::cout << "CLOSED" << std::endl;
    return 0;
}
Address: 127.0.0.1
Port: 80
Scanning 127.0.0.1...
Port 80 : OPEN

请记住,127.0.0.1 等同于 localhost。另外,您的计算机上的 80 端口可能不开放。它只在我这里开放,因为我运行的是 Apache HTTP 服务器(80 端口是通常用于 HTTP,即网站的主要端口;另一个常用的 HTTP 端口是 8080)。

此外,我们也可以轻松地在 Nmap 的“ScanMe”页面上测试此代码。
Address: scanme.nmap.org
Port: 80
Scanning scanme.nmap.org...
Port 80 : OPEN
这次可能需要一些时间,因为您不是扫描自己的计算机,而是通过 Internet 连接到另一台计算机。

端口如雨后春笋


一次扫描一个端口很乏味,我们希望让用户扫描很多端口。一种方法是让用户输入任意数量的端口,然后一次性扫描所有这些端口。这样做的问题是用户可能想扫描很多端口,而不得不一一输入。我们也可以让用户指定一个端口范围,例如 0-100,但那样他们就无法指定该范围之外的值。我们将做得更好,让他们同时做到这两点——在列表中指定范围和单独的端口,例如:“80,8080”;一个范围,例如:“20-80”;或者一个包含范围的列表:“20-80,8080”。此代码由于使用了模板、std::vector、std::stringstream 和 C++11 基于范围的 for 循环而变得相当复杂,因此,如果您跳过了“先决条件”部分且不知道如何使用它们,请回到本文的顶部进行学习。否则,请继续阅读。

首先我们需要一个分割字符串的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Splits a string into tokens arround a delimiter (default: space),
// optionally allowing empty tokens.
static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}


端口的数据类型将是字符串,但我们想要整数,所以我们还需要一个将字符串转换为整数的函数。
1
2
3
4
5
6
7
8
// Converts a string to an integer.
static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}


如果我们有一个端口范围,我们将需要一个函数来生成该范围内的所有值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Swaps two values.
template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

// Generates a vector containing a range of values.
template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}


最后,我们需要一个函数来使用上述函数实际解析端口列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Parses a list of ports containing numbers and ranges
static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    // Split list items.
    for (const std::string& token : split(list, ',')) {
        // Split ranges.
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            // Only one value (add to end of 'ports').
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            // Two values (range - add everything in that range).
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}


我们的最终程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <SFML/Network.hpp>
#include <sstream>
#include <string>
#include <vector>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}


static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}

static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}

template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}

static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    for (const std::string& token : split(list, ',')) {
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}

int main()
{
    std::string address;
    std::string port_list;
    std::vector<int> ports;
    std::cout << "Address: " << std::flush;
    std::getline(std::cin, address);
    std::cout << "Port: " << std::flush;
    std::getline(std::cin, port_list);
    ports = parse_ports_list(port_list);
    std::cout << "Scanning " << address << "...\n";
    for (int port : ports) {
        std::cout << "Port " << port << " : ";
        if (port_is_open(address, port))
            std::cout << "OPEN\n";
        else
            std::cout << "CLOSED\n";
    }
    std::cout << std::flush;
    return 0;
}
Address: 127.0.0.1
Port: 20-80,8080
Scanning 127.0.0.1...
Port 20 : CLOSED
Port 21 : CLOSED
Port 22 : OPEN
Port 23 : CLOSED
Port 24 : CLOSED
Port 25 : CLOSED
Port 26 : CLOSED
Port 27 : CLOSED
Port 28 : CLOSED
Port 29 : CLOSED
Port 30 : CLOSED
Port 31 : CLOSED
Port 32 : CLOSED
Port 33 : CLOSED
Port 34 : CLOSED
Port 35 : CLOSED
Port 36 : CLOSED
Port 37 : CLOSED
Port 38 : CLOSED
Port 39 : CLOSED
Port 40 : CLOSED
Port 41 : CLOSED
Port 42 : CLOSED
Port 43 : CLOSED
Port 44 : CLOSED
Port 45 : CLOSED
Port 46 : CLOSED
Port 47 : CLOSED
Port 48 : CLOSED
Port 49 : CLOSED
Port 50 : CLOSED
Port 51 : CLOSED
Port 52 : CLOSED
Port 53 : OPEN
Port 54 : CLOSED
Port 55 : CLOSED
Port 56 : CLOSED
Port 57 : CLOSED
Port 58 : CLOSED
Port 59 : CLOSED
Port 60 : CLOSED
Port 61 : CLOSED
Port 62 : CLOSED
Port 63 : CLOSED
Port 64 : CLOSED
Port 65 : CLOSED
Port 66 : CLOSED
Port 67 : CLOSED
Port 68 : CLOSED
Port 69 : CLOSED
Port 70 : CLOSED
Port 71 : CLOSED
Port 72 : CLOSED
Port 73 : CLOSED
Port 74 : CLOSED
Port 75 : CLOSED
Port 76 : CLOSED
Port 77 : CLOSED
Port 78 : CLOSED
Port 79 : CLOSED
Port 80 : OPEN
Port 8080 : CLOSED

就这样!我们现在有了一个成功的端口扫描器,它可以扫描用户所需的任意数量的端口。

再扫描几个端口


我们可以通过更改以下代码来修改我们的程序,使其不显示关闭的端口:
89
90
91
92
93
94
95
96
    std::cout << "Scanning " << address << "...\n";
    for (int port : ports) {
        std::cout << "Port " << port << " : ";
        if (port_is_open(address, port))
            std::cout << "OPEN\n";
        else
            std::cout << "CLOSED\n";
    }


89
90
91
92
93
    std::cout << "Showing open ports on " << address << "...\n";
    for (int port : ports) {
        if (port_is_open(address, port))
            std::cout << "Port " << port << " : OPEN\n";
    }

这使得输出更加清晰。扫描我计算机上的所有端口。
Address: localhost
Port: 0-65535
Showing open ports on localhost...
Port 22 : OPEN
Port 53 : OPEN
Port 80 : OPEN
Port 139 : OPEN
Port 445 : OPEN
Port 631 : OPEN
Port 3306 : OPEN
Port 17500 : OPEN
请注意,共有 65535 个端口。如果我们使用先前版本的端口扫描器扫描所有端口,它将生成 65,535 行输出,这将使得获取所需信息非常困难。

我们可以通过在文件顶部添加 #include <iomanip> ,在主函数中的 for 循环之前添加 size_t width = digits(maximum(ports));,然后更改显示端口状态的行以 std::cout << "Port " << std::setw(width) << port << " : OPEN\n"; 来漂亮地格式化我们的输出。您还需要在 main 函数之前添加这两个函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gets the maximum value in a vector.
template <typename T>
static T maximum(const std::vector<T>& values)
{
    T max = values[0];
    for (T value : values) {
        if (value > max)
            max = value;
    }
    return max;
}

// Counts the digits in a number.
template <typename T>
static size_t digits(T value)
{
    size_t count = (value < 0) ? 1 : 0;
    if (value == 0)
        return 0;
    while (value) {
        value /= 10;
        ++count;
    };
    return count;
}


现在输出将得到很好的格式化。
Address: localhost
Port: 0-65535
Showing open ports on localhost...
Port    22 : OPEN
Port    53 : OPEN
Port    80 : OPEN
Port   139 : OPEN
Port   445 : OPEN
Port   631 : OPEN
Port  3306 : OPEN
Port 17500 : OPEN
Port 35723 : OPEN
Port 43351 : OPEN

这是我们改进的端口扫描器的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <SFML/Network.hpp>
#include <sstream>
#include <string>
#include <vector>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::SocketTCP().connect(address, port) == sf::Socket::Done);
}

static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}

static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}

template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}

static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    for (const std::string& token : split(list, ',')) {
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}

template <typename T>
static T maximum(const std::vector<T>& values)
{
    T max = values[0];
    for (T value : values) {
        if (value > max)
            max = value;
    }
    return max;
}

template <typename T>
static size_t digits(T value)
{
    size_t count = (value < 0) ? 1 : 0;
    if (value == 0)
        return 0;
    while (value) {
        value /= 10;
        ++count;
    };
    return count;
}

int main()
{
    std::string address;
    std::string port_list;
    std::vector<int> ports;
    std::cout << "Address: " << std::flush;
    std::getline(std::cin, address);
    std::cout << "Port: " << std::flush;
    std::getline(std::cin, port_list);
    ports = parse_ports_list(port_list);
    std::cout << "Showing open ports on " << address << "...\n";
    size_t width = digits(maximum(ports));
    for (int port : ports) {
        if (port_is_open(address, port))
            std::cout << "Port " << std::setw(width) << port << " : OPEN\n";
    }
    std::cout << std::flush;
    return 0;
}


您可能希望能够轻松地在脚本中使用该程序,并从其他程序轻松运行它,在这种情况下,您可以修改它以使用命令行来获取地址和端口信息。下面是一个修改后的 main 函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(int argc, char* argv[])
{
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " address port(s)\n"
                  << "Examples:\n"
                  << "\t" << argv[0] << " 127.0.0.1 80\n"
                  << "\t" << argv[0] << " localhost 80,8080\n"
                  << "\t" << argv[0] << " 192.0.43.10 0-65535\n"
                  << "\t" << argv[0] << " example.com 0-21,80,8080"
                  << std::endl;
        std::exit(EXIT_FAILURE);
    }
    std::string address = argv[1];
    std::vector<int> ports = parse_ports_list(std::string(argv[2]));
    std::cout << "Showing open ports on " << address << "...\n";
    size_t width = digits(maximum(ports));
    for (int port : ports) {
        if (port_is_open(address, port))
            std::cout << "Port " << std::setw(width) << port << " : OPEN\";
    }
    std::cout << std::endl;
    return 0;
} 


下面是一个允许两者同时使用的版本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(int argc, char* argv[])
{
    std::string address;
    std::vector<int> ports;
    if (argc == 3) {
        address = argv[1];
        ports = parse_ports_list(std::string(argv[2]));
    } else {
        std::string port_list;
        std::cout << "Address: " << std::flush;
        std::getline(std::cin, address);
        std::cout << "Port: " << std::flush;
        std::getline(std::cin, port_list);
        ports = parse_ports_list(port_list);
    }
    std::cout << "Showing open ports on " << address << "...\n";
    size_t width = digits(maximum(ports));
    for (int port : ports) {
        if (port_is_open(address, port))
            std::cout << "Port " << std::setw(width) << port << " : OPEN\n";
    }
    std::cout << std::flush;
    return 0;
}


这是端口扫描器的最终(目前)版本,它允许命令行和交互式调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <SFML/Network.hpp>
#include <sstream>
#include <string>
#include <vector>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::SocketTCP().connect(address, port) == sf::Socket::Done);
}

static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}

static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}

template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}

static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    for (const std::string& token : split(list, ',')) {
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}

template <typename T>
static T maximum(const std::vector<T>& values)
{
    T max = values[0];
    for (T value : values) {
        if (value > max)
            max = value;
    }
    return max;
}

template <typename T>
static size_t digits(T value)
{
    size_t count = (value < 0) ? 1 : 0;
    if (value == 0)
        return 0;
    while (value) {
        value /= 10;
        ++count;
    };
    return count;
}

int main(int argc, char* argv[])
{
    std::string address;
    std::vector<int> ports;
    if (argc == 3) {
        address = argv[1];
        ports = parse_ports_list(std::string(argv[2]));
    } else {
        std::string port_list;
        std::cout << "Address: " << std::flush;
        std::getline(std::cin, address);
        std::cout << "Port: " << std::flush;
        std::getline(std::cin, port_list);
        ports = parse_ports_list(port_list);
    }
    std::cout << "Showing open ports on " << address << "...\n";
    size_t width = digits(maximum(ports));
    for (int port : ports) {
        if (port_is_open(address, port))
            std::cout << "Port " << std::setw(width) << port << " : OPEN\n";
    }
    std::cout << std::flush;
    return 0;
}


随意出于任何目的使用或修改以上所有代码(免责声明:对于使用我的代码执行的任何恶意行为,我概不负责)。

您可以找到我编写的原始程序的源代码,该程序在编写本文时演变成了上述代码。该程序是在 FreeBSD 许可下分发的。

待办事项

附件:[cppscan.cpp] [cppscan.h] [main.cpp]