C++检查TCP端口

10

我已经用C++编写了一个程序,我需要检查一个TCP端口是否真的是空的。

这是函数:

int checkport(char* host, char* port, int timeout)
{
    int sock;
    struct sockaddr_in sin;
    int result = 0;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_family = AF_INET;
    sin.sin_port = htons(atoi(port));
    sin.sin_addr.s_addr = inet_addr(host);
    fd_set fdset;
    struct timeval tv;
    fcntl(sock, F_SETFL, O_NONBLOCK);

    int connect_result = connect(sock,(struct sockaddr*)(&sin),sizeof(struct sockaddr_in));
    FD_ZERO(&fdset);
    FD_SET(sock, &fdset);
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    int select_result = select(sock + 1, NULL, &fdset, NULL, &tv);

    // printf("connect = [%d] | select = [%d]\n",connect_result,select_result);

    if(select_result == 1)
    {
        int so_error;
        socklen_t len = sizeof so_error;
        getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);

        // printf("so_error = [%d] \n",so_error);

        if(so_error == 0)
        {
            //Is connected
            if(hitdebug >= 4) puts("CONNECTED");
            if(hitdebug >= 2) printf("[%s:%s] OPEN\n",host,port);

            result = 1;
        }
        else
        {
            if(hitdebug >= 4) puts("CONNECT_ERROR_1");

            result = 0;

        }
    }
    else
    {
        if(hitdebug >= 4) puts("CONNECT_ERROR_2");

        result = 0;
    }

    close(sock);

    return result;
}

问题在于它仅检测LISTEN端口,我希望一个端口能够100%准确地检测ESTABLISHED,TIME_WAIT等状态……

connect = [-1] | select = [1]
so_error = [111]
50983 TEST=[0]
tcp        0      0 1.2.3.4:50983       4.4.4.4:22       ESTABLISHED


connect = [-1] | select = [1]
so_error = [111]
43343 TEST=[0]
tcp        0      0 1.2.3.4:43343       4.4.4.4:22       ESTABLISHED


connect = [-1] | select = [1]
so_error = [0]
64000 TEST=[1]
tcp        0      0 1.2.3.4:64000       0.0.0.0:*               LISTEN
tcp        0      0 1.2.3.4:47669       1.2.3.4:64000       TIME_WAIT


connect = [-1] | select = [1]
so_error = [111]
54674 TEST=[0]


connect = [-1] | select = [1]
so_error = [111]
54665 TEST=[0]

对我来说,空闲端口意味着当我执行 netstat -an | grep 12345 | wc -l 命令并看到输出为0时,甚至没有FIN_WAIT状态。

我该如何实现这一点?

谢谢。


你可能需要指定你使用的操作系统。C++标准中尚未具备任何网络相关的内容(这很疯狂)。尽管Unix类家族之间存在相似之处,但仍有很大的差异。 - Passer By
1
尝试使用bind绑定端口进行监听 - 如果失败(默认情况下关闭了SO_REUSEADDR),则表示套接字正在活动中或处于等待状态之一。 - Useless
1
为什么不解析netstat输出? - Damien
你这样做的原因是什么?正如n.m.所说,会出现竞态条件。 - seand
竞争条件...你说得对,我别无选择,我想从列表中做一个代理检查器。 - Damian
那是什么意思?你要启动的代理是什么,如何运行它?它能继承现有进程的绑定套接字吗? - Useless
2个回答

16
if (the_port_is_free(port_number)) {
   /* is the port free at this point? */
}

正确答案是“谁知道呢?”当控件在函数the_port_is_free中时,它肯定在一纳秒之前是免费的,但现在它是否免费呢?周围有许多其他进程,也许其中一个在半纳秒之前就占用了端口?

这被称为竞态条件。每个程序员都应该理解这个概念。

正是因为这个问题,你的操作系统没有提供the_port_is_free: 它只会给你错误的期望,而不是正确的答案。你无法知道现在端口是否空闲,更重要的是,当你决定绑定它时,在一纳秒后它是否仍然空闲。唯一的方法是尝试绑定端口。

这种方法适用于很多情况。每当有任何共享资源的时候,通常都没有一个函数告诉你可以抓取它。唯一的方法是尝试获取。


我对 will be free in a nanosecond when 感到高兴,我理解你的观点,我正试图制作一个代理检查器,我的问题是程序无法检测到 ESTABLISHEDWAITFIN_WAIT,你明白我的意思吗?对我来说,少一毫微秒或多一毫微秒都不是问题。 - Damian
@Damian 不好意思,我完全不明白你的意思。你为什么需要这个?你会用这个答案做什么? - n. m.
我理解竞争条件的概念,我只需要让我的函数检测“ESTAB”、“WAIT”、“FIN_WAIT”,就可以了。现在它只能检测“LISTENING”。 - Damian
为什么需要检测所有这些状态?检测到这些状态后你会做什么?为什么不能只是打开代理并查看是否失败? - n. m.
我必须在打开之前检查端口,真的需要这样做,为什么很复杂,无法解释,但确实需要这样做。 - Damian

8

尝试绑定端口以进行监听 - 如果它失败了(使用默认的SO_REUSEADDR关闭时),那么套接字要么正在使用中,要么在等待状态之一。


它会检测ESTAB吗? - Damian
在我执行 bind() 后,如何快速解绑并释放端口,以便我可以在其上打开代理? - Damian
如果bind成功,您已经绑定了端口并知道它是空闲的,这正是您想要做的。如果您关闭并重新绑定端口,则存在不可避免的竞争条件。您可以安全地使用绑定的端口直接进行操作。那么这个代理是什么,为什么您不能让它执行bind并查看是否失败? - Useless
1
因为端口在机器上被所有进程共享。所以在你关闭它并启动代理之间,其他一些进程可能会绑定该端口。除非你的代理是进程内代码,并且可以避免首先关闭绑定的套接字,这就是我问代理是什么的原因。明白我的意思吗? - Useless
1
那样做是没有帮助的。竞态条件是固有的。唯一安全确认套接字是否可供某个进程使用的方法(我假设它是一个进程而不是一个库,尽管你仍然没有回答这一点)是让该进程成功绑定套接字并开始使用它。就这样。释放套接字会导致它再次被窃取。仅仅运行netstat或重现它,允许套接字在您检查和代理启动之间被窃取。 - Useless
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接