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










### 实现ICMP Ping功能 为了在 Linux 中使用 C 语言实现 ICMP Ping 功能,程序需要创建原始套接字并发送 ICMP 请求消息给目标主机。下面是一个简化版本的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUFFER_SIZE 60 void create_icmp_packet(char *buffer, int id) { struct icmp { unsigned char type; unsigned char code; unsigned short checksum; unsigned short identifier; unsigned short sequence_number; } __attribute__((packed)) packet; packet.type = 8; /* Echo request */ packet.code = 0; packet.checksum = 0; packet.identifier = htons(id); packet.sequence_number = htons(1); memcpy(buffer, &packet, sizeof(packet)); } unsigned short calculate_checksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short answer = 0; unsigned short *w = addr; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *) (&answer) = *(u_char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "Usage: %s <IP address> <identifier>\n", argv[0]); exit(EXIT_FAILURE); } const char *target_ip = argv[1]; int id = atoi(argv[2]); int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sockfd < 0) { perror("socket"); exit(EXIT_FAILURE); } char buffer[BUFFER_SIZE]; memset(buffer, '\0', BUFFER_SIZE); create_icmp_packet(buffer, id); struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; inet_pton(AF_INET, target_ip, &(dest_addr.sin_addr)); sendto(sockfd, buffer, sizeof(struct icmp), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); close(sockfd); printf("Ping sent to IP: %s with ID: %d\n", target_ip, id); } ``` 上述代码展示了如何构建一个简单的 ICMP echo 请求报文,并通过原始套接字将其发送到指定的目标地址[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值