您的位置:首页 > 运维架构 > Linux

C语言实现ping功能(Linux & Mac OS系统下)有注释

2017-12-21 17:29 603 查看
#include <stdio.h>

#include <string.h>

#include <sys/socket.h>//socket()

#include <stdlib.h>

#include <unistd.h>//getpid

#include <arpa/inet.h>//IPPROTO_ICMP

#include <sys/time.h>

#include <netinet/ip.h>

#include <netinet/ip_icmp.h>//ICMP_ECHO

float timediff(struct timeval * recTime,struct timeval * nowTime){

    struct timeval sub = *nowTime;

    

    if ((sub.tv_usec -= recTime->tv_usec) <
0)

    {

        --(sub.tv_sec);

        sub.tv_usec += 1000000;

    }

    sub.tv_sec -= recTime->tv_sec;

    

    return sub.tv_sec *
1000.0 + sub.tv_usec / 1000.0;
//转换单位为毫秒

}

unsigned short checkSum(unsignedshort * icmp,int size){

    unsigned int sum =0;

    while (size>1) {

        sum = sum + *icmp;

        icmp += 1;         
//这里不加2是因为short类型指针每次移动2字节

        size = size - 2;   
//16位的方式求和

    }

    if (size == 1) {

        sum = sum + *icmp;

    }

    //加完了如果有进位就,一定是第16位是1,让低位加1即可

    sum = (sum >> 16) + (sum &
0xffff);

    //只有有符号数有反码,无符号数没有反码的概念

    return (unsignedshort)~sum;

}

void packIcmp(char * buf,int sequence){

    struct icmp * icmp = (struct icmp *)buf;

    icmp->icmp_type = ICMP_ECHO;

    icmp->icmp_code = 0;

    icmp->icmp_cksum = 0;

    icmp->icmp_id = getpid();

    icmp->icmp_seq = sequence;

    gettimeofday((struct timeval *)(icmp->icmp_data),0);

    icmp->icmp_cksum = checkSum((unsigned
short *)icmp, sizeof(struct icmp));

}

int unpack(char * buf,ssize_t size,char * addr){

    struct ip * ip;

//    struct MyIcmp * icmp;

    struct icmp * icmp;

    int ipHLen;

    int icmpLen;

    struct timeval t;

    float time;

    //解析ip报文只为了找到icmp起始位置:

    ip = (struct ip *)buf;

    ipHLen = ip->ip_hl << 2;//因为首部长度以4字节位单位,但计算机以字节为单位

    //找到icmp报文的起始位置:

    icmp = (struct icmp *)(buf + ipHLen);

    //解析icmp报文:

    icmpLen = (int)(size-ipHLen);

    if (icmpLen <
8) {

        printf("ICMP报文长度小于8!\n");

        return 0;

    }

    //判断是不是回显报文:

    if (icmp->icmp_type != ICMP_ECHOREPLY || icmp->icmp_id != getpid()) {

        printf("不是回显ICMP报文!\n");

        return 0;

    }

    //解析时间:

    gettimeofday(&t, 0);//0表示不使用时区

    time = timediff((struct timeval *)icmp->icmp_data, &t);

    //解析打印:

    printf("from %s: icmp_seq=%d ttl=%d time=%f ms \n",addr,icmp->icmp_seq,ip->ip_ttl,time);

    return
1;

}

int main(int argc,const
char * argv[]) {

    struct sockaddr_in sendAdd;

    memset(&sendAdd, 0,
sizeof(sendAdd));

    unsigned int recAddInt =sizeof(struct sockaddr_in);

    char recBuf[128];

    char sendBuf[128];

    //清空sendIcmp缓冲区的数据,清零:

    memset(recBuf,0,128);

    memset(sendBuf,0,128);

    

    int sockfd;

    int sequence =
0;

    ssize_t recNum;

    

    in_addr_t inadd;

    char input[20];

    char * pInput;

    

    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) {

        printf("原始套接字创建出错!\n");

        exit(1);//异常退出

    }

    //设置目的IP:

    printf("请输入要Ping的地址(点分十进制):\n");

    scanf("%s",input);

    pInput = input;

    inadd = inet_addr(pInput);//将点分十进制转换成数字

    //填充sendAdd结构体:

    sendAdd.sin_addr.s_addr = inadd;

    sendAdd.sin_family = AF_INET;

    printf("IP: %s \n",inet_ntoa(sendAdd.sin_addr));

    

    

    while(1) {//循环次:

        //封装ICMP报文:

        packIcmp(sendBuf,sequence);

        //发送:

        //需要将sockaddr_in指针转换成sockaddr

        if ((sendto(sockfd, sendBuf,
64, 0,(struct sockaddr *)&sendAdd,sizeof(sendAdd))) == -1) {

            printf("发送sento函数调用返回-1!\n");

            continue;

        }

        //接收:(需要记录函数返回值,表示收到的字节数)

        if ((recNum = recvfrom(sockfd, recBuf,sizeof(recBuf),
0, (struct sockaddr *)&sendAdd, &recAddInt)) == -1) {

            printf("接收recfrom函数调用返回-1!\n");

            continue;

        }else{

            //解析:

            if (!unpack(recBuf,recNum,inet_ntoa(sendAdd.sin_addr))) {

                printf("unpack失败!\n");

            }

        }

        sleep(1);//等一秒;

        sequence = sequence+1;//icmp报文序号加一

    }

    return
0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: