zoukankan      html  css  js  c++  java
  • Linux下实现ping程序

    今天参照大神的代码实现了一个ping程序。
    总体是先发送一个ping请求,然后循环四次监听返回。

    send_ping函數

    1. 將icmp_hdr(icmp消息的header)的指針指向一片內存空間,然後定義各個屬性。通過memcpy函數將要發送的數據複製到data屬性中。

      再通過sendto函數將icmp數據包發送到指定地址

      sendto函數

      #include <sys/types.h>   
      #include <sys/socket.h>
      int sendto(int socketfd, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);
      

      socketfd:socket套接字描述符

      成功則返回發送的字符數,否則返回-1.

      與之對應的是recvfrom函數

      #include <sys/types.h>   
      #include <sys/socket.h>
      int sendto(int socketfd, void* buffer, int len, unsigned int flags, const struct sockaddr * from, int fromlen);
      

      通過套接字接收信息,存放到buffer中。

    handle_packet函數

    1. recv_reply函數將返回的ICMP消息存儲在recv_buf中,在處理返回結果時,將一個iphdr類型的指針指向這塊內存進行操作。

      這塊內存信息包含的數據有IP信息頭部長度,數據部分長度。數據部分包含了icmp的頭部信息和數據信息。icmp的起始地址便是recvbuf + ip頭的長度。然後要計算校驗和,如果檢驗和不為0,說明信息出現了錯誤。

      然後還要判斷icmp_id和icmp_type。

    贴一下源码

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/time.h>
    #include<unistd.h>   
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<netdb.h>
    #include<errno.h>
    #include<arpa/inet.h>
    #include<signal.h>
    #include<netinet/in.h>
     
    #ifndef _LITTLE_ENDIAN_BITFIELD
    #define _LITTLE_ENDIAN_BITFIELD
    #endif
     
    #define IP_HSIZE sizeof(struct iphdr)   //定义IP_HSIZE为ip头部长度
    #define IPVERSION  4   //定义IPVERSION为4,指出用ipv4
     
     
     
    #define ICMP_ECHOREPLY 0 //Echo应答
    #define ICMP_ECHO      8 //Echo请求
     
    #define BUFSIZE 1500     //发送缓存最大值
    #define DEFAULT_LEN 56   //ping 消息数据默认大小
     
    //数据类型别名
    typedef unsigned char u8;
    typedef unsigned short u16;
    typedef unsigned int u32;
     
    //ICMP消息头部
    
    struct icmphdr
    {
        u8 type;
        u8 code;
        u16 checksum;
        union
        {
            struct
            {
                u16 id;
                u16 sequence;
            }echo;
            
            u32 gateway;
            struct
            {
                u16 unused;
                u16 mtu;
            }frag; //pmtu发现
        }un;
        u32  icmp_timestamp[2];//时间戳
        //ICMP数据占位符
        u8 data[0];
    #define icmp_id un.echo.id
    #define icmp_seq un.echo.sequence
    };
     
    #define ICMP_HSIZE sizeof(struct icmphdr)
    struct iphdr
    {
    #if defined _LITTLE_ENDIAN_BITFIELD
        u8 hlen:4,
    ver: 4;
    #elif defined _BIG_ENFIAN_BITFELD
        u8 ver:4,
    hlen:4;
    #endif
        
        u8 tos;
        u16 tot_len;
        u16 id;
        u16 frag_off;
        u8 ttl;
        u8 protocol;
        u16 check;
        u32 saddr;
        u32 daddr;
    };
    char hello[]="hello this is  a ping test.";
    char *hostname; //被ping的主机
    int  datalen=DEFAULT_LEN;//ICMP消息携带的数据长度
    char sendbuf[BUFSIZE];
    char recvbuf[BUFSIZE];
    int nsent;//发送的ICMP消息序号
    int nrecv;
    pid_t pid;//ping程序的进程pid
    struct timeval recvtime; //收到ICMP应答的时间戳
    int sockfd; //发送和接收原始套接字
    struct sockaddr_in dest;//被ping主机的ip
    struct sockaddr_in from;//发送ping应答消息的主机ip
     
    struct sigaction act_alarm;
    struct sigaction act_int;
     
     
    //设置的时间是一个结构体,倒计时设置,重复倒时,超时值设为1秒
    struct itimerval val_alarm;
    
    void alarm_handler(int);//SIGALRM处理程序
    void int_handler(int);//SIGINT处理程序
    void set_sighandler();//设置信号处理程序
    void send_ping();//发送ping消息
    void recv_reply();//接收ping应答
    u16 checksum(u8 *buf,int len);//计算校验和
    int handle_pkt();//ICMP应答消息处理
    void get_statistics(int ,int);//统计ping命令的检测结果
    void bail(const char *);//错误报告
    
    void send_ping() {
        struct iphdr* ip_hdr;
        struct icmphdr* icmp_hdr;
        
        icmp_hdr = (struct icmphdr*)(sendbuf);
        icmp_hdr->type = ICMP_ECHO;
        icmp_hdr->code = 0;
        icmp_hdr->icmp_id = pid; //icmp_id = un.id,means process id
        icmp_hdr->icmp_seq = nsent++;
        gettimeofday((struct timeval*)icmp_hdr->icmp_timestamp,NULL);
        
        int len = ICMP_HSIZE + strlen(hello);
        icmp_hdr->checksum = 0;
        icmp_hdr->checksum = checksum((u8 *)icmp_hdr, len);
        
        sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&dest, sizeof(dest));
    }
    
    void recv_reply() {
        socklen_t len;
        int n = 0;
        nrecv = 0;
        int errno;
        len = sizeof(from);
        
        while (nrecv < 4) {
            if ((n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&from, &len)) < 0) {
                if (errno == EINTR) {
                    continue;
                }
                bail("recv from error");
            }
            gettimeofday(&recvtime, NULL);
            
            if (handle_pkt())
                continue;
            
            nrecv++;
        }
        get_statistics(nsent, nrecv);
    }
    
    u16 checksum(u8* buf, int len) {
        u32 sum=0;
        u16 *cbuf;
        
        cbuf=(u16 *)buf;
        
        while(len>1)
        {
            sum+=*cbuf++;
            len-=2;
        }
        
        if(len)
            sum+=*(u8 *)cbuf;
        
        sum=(sum>>16)+(sum & 0xffff);
        sum+=(sum>>16);
        
        return ~sum;
    
    }
    
    //handle ip response packet
    int handle_pkt() {
        struct iphdr* ip;
        
        ip = (struct iphdr*)recvbuf;
        
        int ip_hlen = ip->hlen << 2;
        u16 ip_datalen = ntohs(ip->tot_len) - ip_hlen; 
        
        struct icmphdr* icmp;
        icmp = (struct icmphdr*)(recvbuf + ip_hlen);
        
        u16 sum = (u16)checksum((u8*)icmp, ip_datalen);
        
        if (sum) {
            printf("check is not equal to 0
    ");
            return -1;
        }
        
        if (icmp->type != ICMP_ECHOREPLY) {
            return -1;
        }
        if (icmp->icmp_id != pid) {
            return -1;
        }
        
        struct timeval* sendtime = (struct timeval*)icmp->icmp_timestamp;
        double rtt;//round-trip time
        rtt = ((&recvtime)->tv_sec - sendtime->tv_sec) * 1000 + ((&recvtime)->tv_usec - sendtime->tv_usec) / 1000.0;
        
        printf("%dbytes from %s:icmp_seq = %u, ttl = %d, rrt = %.3f ms
    ",ip_datalen, inet_ntoa(from.sin_addr), nsent, ip->ttl, rtt);
        
        return 0;
    }
    
    //signal handler
    void set_sighandler() {
        act_alarm.sa_handler=alarm_handler;
        printf("alarm handle signal: %d
    ", SIGALRM);
        if(sigaction(SIGALRM,&act_alarm,NULL)==-1)  //sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum指所要捕获信号或忽略的信号,&act代表新设置的信号共用体,NULL代表之前设置的信号处理结构体。这里判断对信号的处理是否成功。
            bail("SIGALRM handler setting fails.");
        
        act_int.sa_handler=int_handler;
        printf("int handler handle signal: %d
    ", SIGALRM);
        if(sigaction(SIGINT,&act_int,NULL)==-1)
            bail("SIGALRM handler setting fails.");
    
    }
    
    void get_statistics(int nsent, int nrecv) {
        printf("---%s ping statistics-----
    ",inet_ntoa(dest.sin_addr));
        printf("%d packets transmitted,%d received, %0.0f%% loss
    ",nsent, nrecv, 1.0 * (nsent - nrecv) / nsent * 100);
    }
    
    //error report
    void bail(const char* incident) {
        fputs(strerror(errno), stderr);
        fputs(":", stderr);
        fputs(incident, stderr);
        fputc('
    ', stderr);
        exit(1);
    }
    
    //interupt signal process program
    void int_handler(int sig) {
        get_statistics(nsent, nrecv);
        close(sockfd);
        exit(1);
    }
    
    void alarm_handler(int signo) {
        send_ping();
    }
    
    int main(int argc, char** argv) {
        val_alarm.it_interval.tv_sec = 1;
        val_alarm.it_interval.tv_usec = 0;
        val_alarm.it_value.tv_sec = 0;
        val_alarm.it_value.tv_usec = 1;
        struct hostent* host; //#include <netdb.h>
        int on = 1;
        
        if ((host = gethostbyname(argv[1])) == NULL) {
            perror("can not understand the host name");
            exit(1);
        }
        
        hostname = argv[1];
        memset(&dest, 0, sizeof(dest));
        dest.sin_family = PF_INET;
        dest.sin_port = ntohs(0);
        dest.sin_addr = *(struct in_addr *)host->h_addr_list[0];//h_addr_list is char** type
        
        setuid(getuid());//grant user root authority
        if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
            perror("raw socket created error");
            exit(1);
        }
        
        pid = getpid();
        printf("Pid: %d
    ", pid);
        set_sighandler();
        printf("Ping %s(%s): %d bytes data in ICMP packets.
    ", argv[1], inet_ntoa(dest.sin_addr), datalen);
        
        if ((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1)
            bail("setitimer falied");
        
        recv_reply();
        
        return 0;
    
    }
    
    
    我愿潇洒如鹰,远离地上宿命
  • 相关阅读:
    新概念英语(1-25)Mrs. Smith's Kitchen
    新概念英语(1-23)Which glasses?
    新概念英语(1-21)Whick book
    BZOJ2212: [Poi2011]Tree Rotations(线段树合并)
    BZOJ4773: 负环(倍增Floyd)
    洛谷P1155 双栈排序(贪心)
    洛谷P1024 一元三次方程求解(数学)
    洛谷P1072 Hankson 的趣味题(数学)
    2018.10.26NOIP模拟赛解题报告
    洛谷P2831 愤怒的小鸟(状压dp)
  • 原文地址:https://www.cnblogs.com/lunar-ubuntu/p/12589362.html
Copyright © 2011-2022 走看看