码迷,mamicode.com
首页 > 其他好文 > 详细

Ping程序实现

时间:2014-12-22 22:42:29      阅读:226      评论:0      收藏:0      [点我收藏+]

标签:

/*************************************************************************
    > 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;
        }
    }
}
 
//无限ping
void 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程序实现

标签:

原文地址:http://www.cnblogs.com/ickelin/p/4179082.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!