//定义ICMP报头结构
typedef struct icmp_head{
unsigned char icmp_type; //消息类型
unsigned char icmp_code; //代码
unsigned short icmp_checksum; //校验和
unsigned short icmp_id; //表示请求的id号,通常设置为进程号
unsigned short icmp_sequence; //序列号
unsigned int icmp_timestamp; //时间戳
}ICMP_HEAD,*PICMP_HEAD;
//首先要计算校验和
unsigned short checksum(PICMP_HEAD picmp,int nSize){ //picmp为指向整个报文头部的指针
long sum=0;
unsigned short * pusIcmp=(unsigned short *)picmp;
while(nSize>1){
sum+=*(pusIcmp++);
nSize-=sizeof(USHORT);
}
if(nSize){
sum+= *(unsigned char *)pusIcmp;
}
//高16位与低16位相加,取反得到校验和
sum=(sum>>16)+(sum&&0xffff);
sum+=(sum>>16);
return (unsigned short)~sum; //取反
} //结束校验
int main(int argc, char* argv[]){ //argc是参数个数
//存放windows socket初始化信息,例如版本号,WinSock支持的最高版本
WSADATA wsaData;
SOCKET client;//原始套接字
SOCKADDR_IN destAddr; //套接字地址家族,又称为套接字地址形式
struct hostent * host=NULL; //hostent是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表
int nTimeOut=100000;
char szSendBuffer[sizeof(ICMP_HEAD)+32]={0}; //定义发送缓冲区,32是ICMP数据长度
char szRecvBuffer[1024];
PICMP_HEAD picmp=(PICMP_HEAD)szSendBuffer; //PICMP指向发送缓冲区
if(argc<2){ //如果小于两个参数
printf("请输入目标主机的名称或IP地址!\n");
system("PAUSE");
return 1;
}
//加载动态链接库
WSAStartup(MAKEWORD(2,2),&wsaData); //表示使用WINSOCK2版本
//创建原始套接字
//参数分别代表地址描述,套接口类型描述,套接字使用的协议,返回值代表成功与否
client = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if(client==INVALID_SOCKET){
printf("socket()失败!错误码:%d\n",WSAGetLastError());
system("pause");
}
else{
printf("SOCKET创建成功!");
}
//设置接收超时时间,选项名:接收超时 选项值,char* 选项长度
setsockopt(client,SOL_SOCKET,SO_RCVTIMEO,(const char *)&nTimeOut,sizeof(nTimeOut));
//设置目标主机的地址
host=gethostbyname(argv[1]);
if(host==NULL){
printf("主机名解析失败!\n");
system("pause");
return 1;
}
destAddr.sin_family=AF_INET;
destAddr.sin_addr.S_un.S_addr=*((unsigned long *)host->h_addr);//
destAddr.sin_port=htons(0); //WINDOWS套接口实现将给应用程序分配一个值在1024到5000之间的唯一的端口。
//ICMP_HEAD的封装
picmp->icmp_type=8;
picmp->icmp_code=0;
picmp->icmp_checksum=0;
picmp->icmp_id=(unsigned short)GetCurrentProcessId(); //当前发起请求的进程ID
//封装数据
memcpy((szSendBuffer + sizeof(ICMP_HEAD)),"ABCDABCDABCDABCDABCDABCDABCDABCD",32);
//计算校验和 利用内存copy函数
picmp->icmp_checksum=checksum((PICMP_HEAD)szSendBuffer,sizeof(szSendBuffer));
//向目标主机发送ICMP报文并接收回应报文
struct sockaddr_in fromAddr;
int sizeFrom=sizeof(SOCKADDR_IN);
memset(szRecvBuffer,0,1023); //先清空接收的缓冲区
//向目标主机发送一个ICMP报文
//sendto() 用来将数据由指定的socket 传给对方主机
int sendBytes = sendto(client,szSendBuffer,sizeof(szSendBuffer),0,(SOCKADDR *)&destAddr,sizeof(destAddr));
if(sendBytes==SOCKET_ERROR){
printf("发送错误:错误码%d\n",WSAGetLastError());
system("pause");
return 1;
}
else{
printf("发送ICMP报文成功!发送的字节数:%d\n",sendBytes);
}
//从目标主机接收回应的ICMP报文
int recivBytes = recvfrom(client,szRecvBuffer,sizeof(szRecvBuffer),0,(SOCKADDR *)&fromAddr,&sizeFrom);
if(recivBytes==SOCKET_ERROR){
printf("接收错误:错误码:%d\n",WSAGetLastError());
system("pause");
return 1;
}
else{
printf("接收成功!收到的字节数:%d\n",recivBytes);
PICMP_HEAD PTEMP=(PICMP_HEAD)szRecvBuffer+20; //跳过IP报文头部
DWORD t2=GetTickCount();
int time=t2-picmp->icmp_timestamp;//经过的时间
int datalen=recivBytes-20-sizeof(ICMP_HEAD);//发送的字节数
if(strcmp(inet_ntoa(fromAddr.sin_addr),inet_ntoa(destAddr.sin_addr))==0){ //保持地址一致
printf("来自%s的回复:字节=%d 时间=%d ms\n",inet_ntoa(destAddr.sin_addr),datalen,time);
}
}
system("pause");
return 0;
}
- 明星图片
- 相关文章
-
联系邮箱:mxgf168#qq.com(#改为@)