ICMP测试网络是否可达:
我们都知道为了能够有效的通信,在进行通信之前要先确认通信对象是否可达,如果通信对象是不可达的,那么进行数据的传输是没有意义的,所以我们可以使用ICMP【Internet control manage protocol】来测试是否连通。我们知道ICMP报文是跟着IP报文一起传输的,也就是说ICMP报文时IP中的一部分,而且在请求回显的数据中包含着IP数据包和ICMP报文。
其通信模型是:
所以可以看出,其中每一包至少包含32个字节,其中的20字节的IP首部以及12字节的ICMP报文,所以其IP首部的数据结构是:
struct IP_Head{
unsigned char versionandLenght;
unsigned char serviceType;
unsigned short totalLenght;
unsigned short IPId;
unsigned short frag_and_flags;
unsigned char TTL;
unsigned char protocol;
unsigned short checkNum;
unsigned int souIP;
unsigned int dstIP;
};
其中ICMP的结构是:
struct ICMP_Head{
BYTE icmpType;
BYTE icmpCode;
USHORT checkNum;
USHORT icmpId;
USHORT icmpSeq;
ULONG timeStamp;
};
下面是对应的代码:
1:加载DLL:#pragmacomment(lib,"ws2_32.lib")
2:创建socket,创建之前要先配置环境如:
WSAStartup(MAKEWORD(2,2),&wsadata);
socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
3:创建目标地址:
h=gethostbyname(argv[1]);
memcpy(&(destAddr.sin_addr),h->h_addr,h->h_length);
destAddr.sin_family=h->h_addrtype;
4:设置延迟:
setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));
setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&recvtime,sizeof(recvtime));
5:创建ICMP报文:
Char buf[MAX_PACKET];
ICMP_Head *icmphead=NULL;
char *datapart=NULL;
icmphead = (ICMP_Head*)buf;
icmphead->icmpType =ICMP_PROTOCOL;
icmphead->icmpCode =0;
icmphead->checkNum =0;
icmphead->icmpId =(USHORT)GetCurrentProcessId();
icmphead->timeStamp=0;
icmphead->icmpSeq=0;
datapart = buf + sizeof(ICMP_Head);
for(inti=0;i<(DEF_PACKET_SIZE-sizeof(ICMP_Head));i++,datapart++)
*datapart=NULL;
6:发送ICMP报文:
sendto(sockfd,sendbuf,DEF_PACKET_SIZE,0,(sockaddr*)&destAddr,sizeof(sockaddr));
7:获取回显信息:
recvfrom(sockfd,recvbuf,MAX_PACKET,0,(sockaddr*)&fromAddr,&len);
8:解析回显信息:
IP_Head*idHead=(IP_Head*)recvbuf;
ICMP_Head*icmp_head=(ICMP_Head*)(recvbuf+sizeof(IP_Head));
9:关闭socket:
Closesocket(sockfd);
WSACleanup();
在此次的设计过程中,socket的创建在主函数中创建
ICMP报文的初始化在fill_ICMP()函数中初始化
发送报文和接收报文在process_ICMP()函数中
解析报文在decode_ICMP()函数中
校验和计算在checksum函数中
整个程序的源代码是:
#include <iostream>
#include <WINSOCK2.H>
#pragma comment(lib,"ws2_32.lib")
#define ICMP_PROTOCOL 8
#define ICMP_CODE 0
#define ICMP_MIN 8
#define DEF_PACKET_SIZE 32
#define DEF_PACKET_NUMBER 10
#define MAX_PACKET 64
using namespace std;
/************************************************************************/
/* 下面的是IP首部的20个字节,这在接收ICMP回显时使用 */
/************************************************************************/
struct IP_Head{
unsigned char versionandLenght;
unsigned char serviceType;
unsigned short totalLenght;
unsigned short IPId;
unsigned short frag_and_flags;
unsigned char TTL;
unsigned char protocol;
unsigned short checkNum;
unsigned int souIP;
unsigned int dstIP;
};
/************************************************************************/
/* 下面是ICMP的协议信息 */
/************************************************************************/
struct ICMP_Head{
BYTE icmpType;
BYTE icmpCode;
USHORT checkNum;
USHORT icmpId;
USHORT icmpSeq;
ULONG timeStamp;
};
float recv_packet=0;
void fill_ICMP(char *buf){
ICMP_Head *icmphead=NULL;
char *datapart=NULL;
icmphead = (ICMP_Head*)buf;
icmphead->icmpType =ICMP_PROTOCOL;
icmphead->icmpCode =0;
icmphead->checkNum =0;
icmphead->icmpId = (USHORT)GetCurrentProcessId();
icmphead->timeStamp=0;
icmphead->icmpSeq=0;
datapart = buf + sizeof(ICMP_Head);
for(int i=0;i<(DEF_PACKET_SIZE-sizeof(ICMP_Head));i++,datapart++)
*datapart=NULL;
}
unsigned short checksum(unsigned short *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(unsigned short);
}
if(size )
{
cksum += *(unsigned char*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (unsigned short)(~cksum);
}
void decode_ICMP(const char *recvbuf,int byt,sockaddr_in *from){
recv_packet++;
IP_Head *idHead=(IP_Head*)recvbuf;
ICMP_Head *icmp_head=(ICMP_Head*)(recvbuf+sizeof(IP_Head));
cout<<"Reply from "<<inet_ntoa(from->sin_addr);
cout<<" "<<byt<<" bytes";
cout<<" time: "<<GetTickCount()-icmp_head->timeStamp<<" ms ";
cout<<" seq="<<icmp_head->icmpSeq;
cout<<" TTL="<<static_cast<int>(idHead->TTL);
cout<<endl;
}
void process_ICMP(SOCKET sockfd,sockaddr_in fromAddr,sockaddr_in destAddr){
char recvbuf[MAX_PACKET];
char sendbuf[DEF_PACKET_SIZE];
memset(sendbuf,0,DEF_PACKET_SIZE);
fill_ICMP(sendbuf);
for (int i=0; i<DEF_PACKET_NUMBER; ++i)
{
((ICMP_Head*)sendbuf)->checkNum=0;
((ICMP_Head*)sendbuf)->icmpSeq=i+1;
((ICMP_Head*)sendbuf)->timeStamp= GetTickCount();
((ICMP_Head*)sendbuf)->checkNum=checksum((unsigned short*)sendbuf,32);
int bsend;
bsend=sendto(sockfd,sendbuf,DEF_PACKET_SIZE,0,(sockaddr*)&destAddr,sizeof(sockaddr));
if (bsend==SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout<<"Request timed out."<<endl;
continue;
}
else
{
cout<<"sendto( ) failed:"<<WSAGetLastError();
cout<<endl;
return;
}
}
cout<<"send success!data is:"<<((ICMP_Head*)sendbuf)->icmpSeq;
cout<<" send "<<bsend<<" byte"<<endl;
int len=sizeof(sockaddr);
int brecv=recvfrom(sockfd,recvbuf,MAX_PACKET,0,(sockaddr*)&fromAddr,&len);
if (brecv==SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout<<"Request timed out."<<endl;
continue;
}
cout<<"recvfrom() failed: "<<WSAGetLastError()<<__LINE__<<endl;
cout<<endl;
return;
}
decode_ICMP(recvbuf,brecv,&fromAddr);
Sleep(1000);
}
cout<<"all send "<<DEF_PACKET_NUMBER<<" packet ,obtain "<<recv_packet<<endl;
cout<<"loss packet is %"<<(1-recv_packet/DEF_PACKET_NUMBER)*100<<endl;
}
int main(int argc,const char *argv[]){
WSADATA wsadata;
SOCKET sockfd;
hostent *h;
sockaddr_in fromAddr,destAddr;
if (WSAStartup(MAKEWORD(2,2),&wsadata)!=0)
{
cout<<"WSAStartup fail!"<<endl;
cout<<"errno="<<WSAGetLastError()<<endl;
return 1;
}
if (argc<=1)
{
cout<<"please input params!"<<endl;
return 1;
}
h=gethostbyname(argv[1]);
if (h==NULL)
{
cout<<"gethosybyname error!"<<endl;
return 1;
}
memcpy(&(destAddr.sin_addr),h->h_addr,h->h_length);
destAddr.sin_family=h->h_addrtype;
//destAddr.sin_port=htons(0);
cout<<"address:"<<inet_ntoa(destAddr.sin_addr)<<endl;
sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if (sockfd==SOCKET_ERROR)
{
cout<<"socket fail!"<<endl;
return 1;
}
int timeout=1000;
int bread=setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout));
if (bread==SOCKET_ERROR)
{
cout<<"setsockopt read fail!"<<endl;
return 1;
}
int recvtime=500;
int bwrite=setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&recvtime,sizeof(recvtime));
if (bwrite==SOCKET_ERROR)
{
cout<<"setockopt write fail!"<<endl;
return 1;
}
process_ICMP(sockfd,fromAddr,destAddr);
closesocket(sockfd);
WSACleanup();
return 0;
}