⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pingcode.c

📁 linux 下 ping 命令的简单实现
💻 C
字号:
# include       <stdio.h>
# include       <fcntl.h>
# include       <errno.h>
# include       <signal.h>
# include       <sys/types.h>
# include       <sys/socket.h>
# include       <sys/time.h>
# include       <netinet/in.h>
# include       <arpa/inet.h>
# include       <netdb.h>

# define        ICMP_ECHO       8       /* 定义icmp echo */
# define        ICMP_ECHOREPLY  0       /* 定义icmp echo reply */
# define        ICMP_HEADSIZE   8       /* 定义icmp 包头的大小 */
# define        IP_HEADSIZE     20      /* 定义ip 包头的大小 */

	/* 以下是定义的两个用来比较大小的宏 */
# define		  MAX(a,b)	    ((a) > (b))? (a):(b)
# define        MIN(a,b)        ((a) > (b))? (b):(a)

typedef struct  tagIpHead               /* 定义icmp 包头数据结构 */
{
        u_char          ip_verlen;      /* ip version 和 ip header 的长度 */
        u_char          ip_tos;         /* service的ip类型 */
        u_short         ip_len;         /* ip包的长度 */
        u_short         ip_id;          /* ip包的标识号 */
        u_short         ip_fragoff;     /* ip包的起始地址 */
        u_char          ip_ttl;         /* ip包的存活时间 */
        u_char          ip_proto;       /* ip包的协议类型 */
        u_short         ip_chksum;      /* ip包头的检验长度 */
        u_long          ip_src_addr;    /* ip包的源地址 */
        u_long          ip_dst_addr;    /* ip包的目标地址 */
} IPHEAD;

typedef struct tagIcmpHead              /* icmp 头结构 */
{
        u_char          icmp_type;      /* icmp 服务类型 */
                                        /* 8 echo require, 0  echo reply */
        u_char          icmp_code;      /* icmp包头代码 */
        u_short         icmp_chksum;    /* icmp包头的检验和 */
        u_short         icmp_id;        /* icmp包的标识号 */
        u_short         icmp_seq;       /* icmp包队列 */
        u_char          icmp_data[1];   /* icmp包数据,指针类型 */
} ICMPHEAD;

/*********************************************************************
		函数ChkSum用来作奇偶校验。
	参数:
u_short * pIcmpData数据起始地址
		int iDataLen为数据长度
	返回值:
		校验结果
*********************************************************************/
u_short ChkSum( u_short * pIcmpData, int iDataLen )
{
        u_short iSum;
        u_short iOddByte;

        iSum = 0;

        while ( iDataLen > 1 ) {        
                iSum = iSum ^ (pIcmpData++);		/* 和下一个数据作异或操作 */
                iDataLen -= 2;
        }

        if ( iDataLen == 1 ) {          /* 剩下的奇数位 */
                iOddByte = 0;
                *((u_char *)&iOddByte) = *(u_char *)pIcmpData;
                iSum ^= iOddByte;
        }

        iSum ^= 0xffff;                 /* 取反操作 */
        return(iSum);
}
/*******************************************************************************
	time_now函数用来返回系统时间。
	返回值:
		系统的当前时间和1970.1.1 00:00:00之间的时间间隔,
		用微妙来表示。
********************************************************************************/
long time_now()
{
        struct timeval now;
        long    lPassed;
        gettimeofday(&now, 0);
        lPassed = now.tv_sec * 1000000 + now.tv_usec;
                                        /* now.tv_sec 中保存了秒数*/
                                        /* now.tv_usec 中保存了微妙数 */
        return lPassed;
}

char*   host;                   /* 目标主机 */
char*   prog;                   /* 程序名称 */
extern  errno;                  /* 系统全局变量 */
long    lSendTime;              /* 数据包的发送时间,在发数据时被改变 */
u_short seq;                    /* icmp包队列 */
int     iTimeOut;               /* 超时时间 */
int     sock, sent, recvd, max, min, total;
                                /* sent : 发送出去的包的数目 */
                                /* recvd: 接收到的包的数目 */
                                /* max, min: 最大和最小的往返时间 */
                                /* total: 总共用时 */
                                /* 用来计算平均值 */
u_long  lHostIp;                /* 主机的ip地址 */
struct sockaddr_in it;          /* 目标主机的信息 */

int ping();
void stat();
main(int argc, char** argv)
{
        struct hostent* h;
        char            buf[200];
        char            dst_host[32];
        int             i, namelen;
        IPHEAD*         pIpHead;
        ICMPHEAD*       pIcmpHead;

        if (argc < 2) {         /* 每一个超时时间后ping一次指定的主机 */
                                /* 缺省的超时时间为1秒 */

                printf("usage: %s [-timeout] host|IP\n", argv[0]);
                exit(0);
        }
        prog = argv[0];
        host = argc == 2 ? argv[1] : argv[2];
        iTimeOut = argc == 2 ? 1 : atoi(argv[1]);

        /* 创建icmp套接字 */

        if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
                perror("socket");
                exit(2);
        }

        /* 设置目标主机信息 */

        bzero(&it, sizeof(it));
        it.sin_family = AF_INET;

        /* 检查主机地址类型 */

        if ( ( lHostIp = inet_addr(host) ) != INADDR_NONE ) {
                                                /* 合法的ip地址 */
                it.sin_addr.s_addr = lHostIp;
                strcpy( dst_host, host );
        } else if ( h = gethostbyname(host) ) {
                                                /* 获取合法的主机名称 */
                                                /* 从本地机上获取*/
                                                /* 或从DNS服务器上获取 */
                bcopy(h->h_addr, &it.sin_addr, h->h_length);
                sprintf( dst_host, "%s (%s)", host,
                         inet_ntoa(it.sin_addr) );
        } else {
                                                /* 错误的ip地址或主机名称 */
                                                /* 在这种情况下程序便退出 */
                fprintf( stderr, "bad IP or host\n" );
                exit(3);
        }
        namelen = sizeof(it);
 
        printf("\nDigger pinging  %s, send %d bytes\n",
                dst_host,
                IP_HEADSIZE + ICMP_HEADSIZE + sizeof(long)
                );
 
        seq = 0;                /* 第一个 icmp_seq = 0 */
        sigset(SIGINT, stat);   /* 按键响应函数,当收到del 或 ctrl+c */
    /*按键操作后便调用 stat函数 */
                                /* 显示完结果,便退出 */
        sigset(SIGALRM, ping);  /* 设置超时处理函数 */
                                /* 收到超时信号后便去调用ping函数 */
        alarm(iTimeOut);        /* 启动计时器 */
                                /* 以秒为单位 */
        ping();
        for ( ;; ) {            /* 等待每个返回的 */
                                /* icmp包,并且处理它 */
                register size;
                register u_char ttl;
                register delta;
                register iIpHeadLen;
 
                /* 阻塞并获取返回的数据包 */
 
                size = recvfrom(sock, buf, sizeof(buf), 0,
                                (struct sockaddr *)&it, &namelen);
                if (size == -1  &&  errno == EINTR) {
                                /* 获取数据时出错或者是系统调用被信号所中断 */
                        continue;
                }
 
                /* 计算数据包的往返时间 */
                /* 用当前系统时间减去发送此包时的系统时间来获得 */
 
                delta = (int)((time_now() - lSendTime)/1000);
 
                /* 获取返回的数据包并检查它的ip头 */
 
                pIpHead = (IPHEAD *)buf;
 
                /* 检查包的大小,如果包太小,那么就不是返回的icmp数据包 */
                /* 这时,便丢弃这个包 */
 
                iIpHeadLen = (int)((pIpHead->ip_verlen & 0x0f) << 2);
                if (size < iIpHeadLen + ICMP_HEADSIZE) {
                        continue;
                }
                ttl = pIpHead->ip_ttl;          /* 数据包的存活时间 */
 
                /* 获取icmp 头信息 */
                pIcmpHead = (ICMPHEAD *)(buf + iIpHeadLen);
 
                /* 不是icmp返回包,丢弃它 */
                if (pIcmpHead->icmp_type != ICMP_ECHOREPLY) {
                        continue;
                }
 
                /* 不是正确的icmp队列号,丢弃它 */
                if (pIcmpHead->icmp_id != seq || pIcmpHead->icmp_seq != seq) {
                        continue;
                }
 
                /* 给每个返回icmp包打印返回信息 */
                sprintf( buf, "icmp_seq=%u bytes=%d ttl=%d",
                        pIcmpHead->icmp_seq, size, ttl );
                fprintf(stderr, "reply from %s: %s time=%d ms\n",
                            host, buf, delta);
 
                /* 计算ping的结果,包括: */
                /* 数据包的最大、最小、平均往返时间 */
                /* 收到的返回icmp数据包的数目 */
                max = MAX(delta, max);
                min = min ? MIN(delta, min) : delta;
                total += delta;
                ++ recvd;
 
                /* 紧接着下一个的icmp数据包 */
 
                ++ seq;
        }
}
 
/*******************************************************************************
	ping函数
		向指定的目标地址发送icmp数据包。
	无参数,无返回值。
	使用全局变量。
*******************************************************************************/
ping()
{
        char    buf[200];
        int     iPacketSize;
 
        /* 设置icmp数据包的头信息 */
 
        ICMPHEAD *pIcmpHead = (ICMPHEAD *)buf;
        pIcmpHead->icmp_type = ICMP_ECHO;
        pIcmpHead->icmp_code = 0;
        pIcmpHead->icmp_id = seq;
        pIcmpHead->icmp_seq = seq;
        pIcmpHead->icmp_chksum = 0;
 
        /* store time information as icmp packet content, 4 bytes */
        /* 你也可以在此存储一些别的信息 */
 
        *((long *)pIcmpHead->icmp_data) = time_now();
 
        iPacketSize = ICMP_HEADSIZE + 4;        /* icmp包的长度 */
 
        /* icmp包数据的奇偶校验 */
 
        pIcmpHead->icmp_chksum = ChkSum((u_short *)pIcmpHead,
                                                iPacketSize );
 
        /* 存储发送包的时间,以便用来计算数据包的往返时间 */
        lSendTime = time_now();
 
        /* 向指定的目标地址发送icmp数据包 */
        if ( sendto(sock, buf, iPacketSize, 0, (struct sockaddr *)&it,
                sizeof(it) ) < 0) {
                perror("send failed");
                exit(6);
        }
 
        /* 总共发送的数据包数目 */
        ++sent;
 
        /* 重新设置计时器 */
        alarm(iTimeOut);
}
 /*******************************************************************************
	stat函数:
		打印结果信息。
*********************************************************************************/
void stat()
{
        if (sent) {
                printf("\n----- %s ping statistics summerized by Digger-----\n"
                        , host );
                printf("%d packets sent, %d packets received, %.2f%% lost\n",
                        sent, recvd, (float)(sent-recvd)/(float)sent*100 );
        }
        if (recvd) {
                printf("round_trip min/avg/max: %d/%d/%d ms\n\n",
                        min, total/recvd, max );
        }
        exit(0);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -