#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;
}