首页 > 代码库 > Ping程序实现

Ping程序实现

/*************************************************************************    > File Name: Ping.c    > Author: ICKelin    > Mail: 18277973721@sina.cn     > Created Time: 2014年12月17日 星期三 04时53分50秒 ************************************************************************/#include <stdio.h>#include <netdb.h>#include <errno.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/time.h>#include <arpa/inet.h>#include <netinet/ip.h>#include <netinet/ip_icmp.h> #include <cmath>#include <vector>#include <numeric>#include <algorithm> const size_t MXLEN = 1500;const size_t DATALEN = 56;const size_t REQLEN = ICMP_MINLEN + DATALEN;const size_t NAMELEN = 50; char dstIP[NAMELEN], dstName[NAMELEN]; //目标IP,目标主机名char sendBuf[REQLEN], recvBuf[MXLEN];  //发送缓存,接收缓冲 sockaddr_in dstAddr;        //目标地址uint32_t nrSend, nrRecv;    //发送包数量,接收包数量std::vector<double> rtts;   //记录每次的rtt timeval startTime, endTime; //开始时间,结束时间 //计算校验和uint16_t calChecksum(uint16_t *buf, size_t len){    uint32_t sum = 0;    while (len > 1) sum += *buf++, len -= 2;    if (len == 1)        sum += (*(uint8_t *)buf) << 4 ;            sum = (sum >> 16) + (sum & 0xFFFF);    sum += sum >> 16;    return (uint16_t)~sum;} //输出统计信息void calStat(int signo){    //计时结束,计算花费的时间    gettimeofday(&endTime, NULL);    int elapse = 1000 * (endTime.tv_sec - startTime.tv_sec) + (endTime.tv_usec - startTime.tv_usec) / 1000;     printf("\n--- %s ping statistics ---\n"           "%u packets transmitted, %u received, %.lf%% packet loss, time %dms\n",           dstName, nrSend, nrRecv, 1 - (double)nrRecv / nrSend, elapse);     //统计rtt信息    std::sort(rtts.begin(), rtts.end());    double mn = *rtts.begin(), mx = *rtts.rbegin();    double avg = accumulate(rtts.begin(), rtts.end(), 0.0) / rtts.size();    double mdev = 0;    for (std::vector<double>::iterator p = rtts.begin(); p != rtts.end(); p++)        mdev += (*p - avg) * (*p - avg);    mdev = pow(mdev / rtts.size(), 0.5);        printf("rtt min/avg/max/mdev = %.3lf/%.3lf/%.3lf/%.3lf ms\n", mn, avg, mx, mdev);     exit(0);} //发送ICMP包void sendRequest(int sockfd){    //包计数    static uint32_t no = 1;     //填充ICMP包内容    icmp *icmp = (struct icmp *)sendBuf;    icmp->icmp_type = ICMP_ECHO;    icmp->icmp_code = 0;    icmp->icmp_cksum = 0;    icmp->icmp_seq = no++;    icmp->icmp_id = getpid();     timeval *tv = (timeval *)icmp->icmp_data;    gettimeofday(tv, NULL);    icmp->icmp_cksum = calChecksum((uint16_t *)icmp, REQLEN);     //发送    int n = sendto(sockfd, sendBuf, REQLEN, 0, (sockaddr *)&dstAddr, sizeof(dstAddr));    if (n <= 0)    {        if (errno == EWOULDBLOCK)            printf("Send timed out.\n");        else            perror("sendto error");    }    else        nrSend++;} //接收ICMP包void recvReply(int sockfd){    socklen_t addrLen = sizeof(dstAddr);    while (true) //接收ICMP应答,无限循环直到成功接收或超时出错。    {        int n = recvfrom(sockfd, recvBuf, sizeof(recvBuf), 0, (sockaddr *)&dstAddr, &addrLen);        if (n <= 0)        {            if (errno == EWOULDBLOCK)                printf("Receive timed out.\n");            else                perror("recvfrom error");            return;        }         ip *ip = (struct ip *)recvBuf;        int iphdrLen = ip->ip_hl << 2;        if ((n -= iphdrLen) < 8)        {            printf("ICMP packets too short.\n");            return;        }         icmp *icmp = (struct icmp *)(recvBuf + iphdrLen);            if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == getpid()) //确实是之前发送包的应答        {            nrRecv++;            timeval *tvSend = (timeval *)icmp->icmp_data, tvRecv;            gettimeofday(&tvRecv, NULL);            double rtt = (tvRecv.tv_sec - tvSend->tv_sec) * 1000 + (tvRecv.tv_usec - tvSend->tv_usec) / 1000.0; //时间差            rtts.push_back(rtt);            printf("%d byte from %s: icmp_req=%u, ttl=%d, time=%.1lf ms\n", n, dstIP, icmp->icmp_seq,  ip->ip_ttl, rtt);            return;        }    }} //无限pingvoid ping(int sockfd){    //扩大缓冲区    int size = 1024 * 50;    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));     //设置超时值    timeval tv = {5, 0};    setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));        //无限ping只能靠ctrl-c结束,设置该信号的响应    signal(SIGINT, calStat);        nrSend = nrRecv = 0;    while (1) //无限发送ICMP请求和接收应答    {        sendRequest(sockfd);        recvReply(sockfd);        sleep(1); //1秒1次    }} int main(int argc, char **argv){    if (argc != 2)    {        printf("Usage: Ping IP/host.\n");        return 0;    }     gettimeofday(&startTime, NULL); //计时开始     int sockfd;    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)    {        perror("Creating socket error");        exit(1);    }                memset(&dstAddr, 0, sizeof(dstAddr));    dstAddr.sin_family = AF_INET;    if (inet_pton(AF_INET, argv[1], &dstAddr.sin_addr) <= 0)    {            //输入的是主机名        hostent *host;        if ((host = gethostbyname(argv[1])) == NULL)        {            perror("Invalid parameter");            exit(1);        }        memcpy((char *)&dstAddr.sin_addr, host->h_addr, sizeof(dstAddr.sin_addr));        inet_ntop(AF_INET, (void *)&dstAddr.sin_addr, dstIP, sizeof(dstIP));        strcpy(dstName, host->h_name);    }    else    {        //输入的是IP地址        memcpy(dstIP, argv[1], sizeof(argv[1]));        memcpy(dstName, argv[1], sizeof(argv[1]));    }     printf("PING %s (%s) %d(%d) bytes of data.\n", dstName, dstIP, DATALEN, REQLEN + 20);     ping(sockfd);     return 0;}

参考链接:http://noalgo.info/810.html

Ping程序实现