ping with ICMP

本文介绍如何使用ICMP协议实现网络可达性测试,包括创建socket、构造ICMP报文、发送及接收报文的过程,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值