Ping的原理和实现

Ping的基本原理是依赖ICMP协议。ICMP包头如下:
在这里插入图片描述
类型和code定义如下:
在这里插入图片描述
在这里插入图片描述

设计思想

  1. 定义IP头部结构
  2. 定义ICMP头部结构
  3. 构造ICPM报文并且计算ICMP的校验和
  4. 通过sendto和recvfrom收发ICMP报文

代码如下

定义通讯包头NetStruct.h

//网络字节序,即大端
//unsigned char header_len : 4;
//为什么不定义成位模式,因为需要进行字节序处理
#pragma pack(push,1)
typedef struct IPHeader
{
private:
	unsigned char version_headerlen;//版本(4) ipv4-4 ipv6-6  首部长度(4) 32(4字节)位为单元在计算 max=60(15*4)字节
public:
	unsigned char tos;//服务类型
	unsigned short total_len;//单位字节,IP头部+数据部分
	unsigned short flag;//标识
private:
	unsigned short flag_sep_offset;//标识3:偏移13
public:
	unsigned char ttl;
	unsigned char proto_type;//1-icmp 2-igmp 6-tcp 17-udp
	unsigned short check_sum;//头部校验和
	unsigned int src_ip;
	unsigned int dest_ip;

	unsigned char version()
	{
		return (version_headerlen >>2) & 0x0f;
	}

	unsigned char header_len()
	{
		return version_headerlen & 0x0f;
	}

}IPHeader, *PIPHeader;


typedef struct ICMPHeader
{
	unsigned char type;
	unsigned char code;
	unsigned short check_sum;
	unsigned short flag;
	unsigned short seq;
	unsigned char data[0];//数据部分
}ICMPHeader, *PICMPHeader;

#pragma pack(pop)

构造ICMP报文并且计算校验和 Ping.cpp

unsigned short CalcCheckSum(unsigned short *pdata,int nSize)
{
	unsigned long check_sum = 0;
	while (nSize > 1)
	{
		check_sum += *pdata++;
		nSize -= sizeof(unsigned short);
	}

	if (nSize)
	{
		check_sum += *(unsigned short *)pdata;
	}

	check_sum = (check_sum >> 16) + (check_sum & 0xffff);
	check_sum += (check_sum >> 16);

	unsigned short result =  (unsigned short)(~check_sum);

	return result;
}

PICMPHeader NewIcmpPack(const void *icmpData, int &len)
{
	unsigned int pack_len = sizeof(ICMPHeader) + len;
	char *pack_buffer = new char[pack_len];
	memset(pack_buffer, 0, pack_len);

	PICMPHeader pHeader = (PICMPHeader)pack_buffer;
	memcpy(pHeader->data, icmpData, len);
	pHeader->type = 8;
	pHeader->code = 0;
	pHeader->flag = 0x1234;
	pHeader->seq = 0;
	pHeader->check_sum = CalcCheckSum((unsigned short *)pack_buffer, pack_len);
	len = pack_len;
	return pHeader;
}

void DeleteIcmpPack(PICMPHeader p)
{
	if(p != NULL)
	{
		delete [](char *)p;
	}
}

//解析icmp数据包
PICMPHeader parseIcmpReply(char *pdata, unsigned int date_len)
{
	PIPHeader pIpHeader = (PIPHeader)pdata;
	unsigned short iHeaderLen = pIpHeader->header_len() * 4;
	//icmp 数据不完整
	if (date_len < iHeaderLen + sizeof(ICMPHeader))
	{
		cout << "ICMP pack data error" <<  endl;
		return NULL;
	}
	PICMPHeader pIcmpHeader = (PICMPHeader)pIpHeader->data;
	return pIcmpHeader;
}

发送和接收ICMP报文

bool ping(const char *szIp, unsigned int timeout)
{
	SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (sock == INVALID_SOCKET)
	{
		cout << "socket create failed:" << WSAGetLastError() << endl;
		return false;
	}

	//如果是linux则timeout为struct timeval
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
	setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));

	sockaddr_in sock_dst = {0};
	sock_dst.sin_family = AF_INET;
	sock_dst.sin_addr.S_un.S_addr = inet_addr(szIp);

	int data_len = 6;
	PICMPHeader pIcmpPack = NewIcmpPack("Hellow", data_len);
	if (SOCKET_ERROR == sendto(sock, (const char *)pIcmpPack, data_len, 0, (sockaddr*)&sock_dst, sizeof(sock_dst)))
	{
		cout << "sendto data failed:" << WSAGetLastError() << endl;
		closesocket(sock);
		DeleteIcmpPack(pIcmpPack );
		return false;
	}
	DeleteIcmpPack(pIcmpPack );
	
	while (true)
	{
		char recv_buf[1024] = "";
		int ret = recvfrom(sock, recv_buf, 1024, 0, NULL, NULL);
		if (ret == SOCKET_ERROR)
		{
			cout << "WSAStartup failed:" << WSAGetLastError() << endl;
		}
		else
		{
			PICMPHeader pIcmpReplay = parseIcmpReply(recv_buf, ret);
			
			//根据协议处理icmp应答
		}

		break;
	}
	closesocket(sock);
	return true;
}

测试程序,通过winshark抓包看一下结果

ping("192.168.34.187", 3000);

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值