在Linux下,C++中最好的ping方法是什么?

9
我需要从C++代码中调用ping命令,并希望轻松读取输出以供进一步利用。
我想到了两种解决方案:
1. 使用fork和pipe,将ping的输出重定向到pipe中,然后解析它。 2. 找一个适合此目的的库,直接使用ping(ip_addresss)函数。
我更倾向于后者,但我没有找到任何明显的标准解决方案。
您会怎么做?
6个回答

9

从教育角度来看,调用外部二进制文件是非常不可取的。特别是对于像发送ICMP回显请求这样简单的任务,您应该学习一些套接字知识。


2
ICMP需要root权限。请参见上文。 - Ken Cole

9
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>


#define PACKETSIZE  64
struct packet
{
    struct icmphdr hdr;
    char msg[PACKETSIZE-sizeof(struct icmphdr)];
};

int pid=-1;
struct protoent *proto=NULL;
int cnt=1;

/*--------------------------------------------------------------------*/
/*--- checksum - standard 1s complement checksum                   ---*/
/*--------------------------------------------------------------------*/
unsigned short checksum(void *b, int len)
{
    unsigned short *buf = b;
    unsigned int sum=0;
    unsigned short result;

    for ( sum = 0; len > 1; len -= 2 )
        sum += *buf++;
    if ( len == 1 )
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
}


/*--------------------------------------------------------------------*/
/*--- ping - Create message and send it.                           ---*/
/*    return 0 is ping Ok, return 1 is ping not OK.                ---*/
/*--------------------------------------------------------------------*/
int ping(char *adress)
{
    const int val=255;
    int i, sd;
    struct packet pckt;
    struct sockaddr_in r_addr;
    int loop;
    struct hostent *hname;
    struct sockaddr_in addr_ping,*addr;

    pid = getpid();
    proto = getprotobyname("ICMP");
    hname = gethostbyname(adress);
    bzero(&addr_ping, sizeof(addr_ping));
    addr_ping.sin_family = hname->h_addrtype;
    addr_ping.sin_port = 0;
    addr_ping.sin_addr.s_addr = *(long*)hname->h_addr;

    addr = &addr_ping;

    sd = socket(PF_INET, SOCK_RAW, proto->p_proto);
    if ( sd < 0 )
    {
        perror("socket");
        return 1;
    }
    if ( setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val)) != 0)
    {
        perror("Set TTL option");
        return 1;
    }
    if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 )
    {
        perror("Request nonblocking I/O");
        return 1;
    }

    for (loop=0;loop < 10; loop++)
    {

        int len=sizeof(r_addr);

        if ( recvfrom(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, &len) > 0 )
        {
            return 0;
        }

        bzero(&pckt, sizeof(pckt));
        pckt.hdr.type = ICMP_ECHO;
        pckt.hdr.un.echo.id = pid;
        for ( i = 0; i < sizeof(pckt.msg)-1; i++ )
            pckt.msg[i] = i+'0';
        pckt.msg[i] = 0;
        pckt.hdr.un.echo.sequence = cnt++;
        pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
        if ( sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0 )
            perror("sendto");

        usleep(300000);

    }

    return 1;
}

/*--------------------------------------------------------------------*/
/*--- main - look up host and start ping processes.                ---*/
/*--------------------------------------------------------------------*/
int main(int argc, char *argv[])
{

    if (ping("www.google.com"))
        printf("Ping is not OK. \n");
    else
        printf("Ping is OK. \n");


    return 0;
}

这仅适用于根用户,因为SOCK_RAW需要根级别权限。在此处阅读有关套接字类型的信息:http://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.progcomc/socket-types.htm - sb32134

4

我会选择你的第一个选项。Linux是围绕着小型、专业化应用程序的概念构建的,这些应用程序非常擅长处理管道通信。你的应用程序不应该包含实现ping的库,因为已经有了内置命令来完成它,而且它的工作表现非常出色!


1
是的,第一种方案似乎是最好的方式。此外,使用 ICMP 协议需要 root 权限。 - Stefan Rogin

3

请查看BusyBox的'ping'源代码 - 您可以使用ping4ping6函数。只需注意GPL。

也可以使用生成的'ping' - 查看popen(2)以获取更简单的API,它还运行一个shell。如果有问题,可以使用pipe + fork + exec


1
这里可以找到更新的ping函数源代码(因为它们已经转移到Git):http://git.busybox.net/busybox/tree/networking/ping.c - Alex Marshall

0

https://github.com/octo/liboping 呢?

    #include <oping.h>

    int main(){
        
        // run ping 100times
        for (uint32_t i=0; i< 100; i++){
            pingobj_t * pingObj = ping_construct();
            ping_host_add(pingObj, "www.gmx.de");

            auto startTime = std::chrono::high_resolution_clock::now();
            auto ret = ping_send(pingObj);
            auto endTime = std::chrono::high_resolution_clock::now();
            if (ret > 0){
                auto duration = (double)std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count()/1000.0;
                std::cout << "success -- ping in " << duration << "ms" << std::endl;
            } else {
                std::cout << "failed" << std::endl;
            }
            ping_destroy(pingObj);
            
            // wait 1sec
            std::this_thread::sleep_for(std::chrono::milliseconds (1000));
        }

    }

liboping 应该在大多数 Linux 系统中存在

  • 安装 liboping-dev(例如:sudo apt install liboping-dev
  • 链接到 liboping

-1
我已经成功地做到了这一点:
我使用 popen 创建了一个管道,fork 和 exec 然后,如果需要,我可以使用 pclose 等待。

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