C++实现基于ICMP协议的ping命令

前言

接上一篇,对ICMP的学习,用C++实现基于ICMP协议的ping工具,用来判断设备的在线状态。
对比ICMP报文格式进行验证学习,再次,将ICMP报文格式粘贴过来,如下:C++实现基于ICMP协议的ping命令_第1张图片

设计思路

  • 定义IP数据包头结构(仅解析时用到)
  • 定义ICMP数据包头结构
  • 发包之前进行ICMP包的校验和
  • 用sendto发送ICMP报文
  • 用recvfrom接收返回的IP数据包
  • 解析出ICMP包进行状态判断

代码

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define  _CRT_SECURE_NO_WARNINGS
#include 
#include 
#pragma comment(lib,"WS2_32")     //链接到WS2_32.lib
using namespace std;

//IP数据包头结构      8位1字节   共20字节
struct IPHeader
{
	unsigned char m_VerHen;                 //4位版本+4位首部长度
	unsigned char m_byTOS;			        //8服务类型
	unsigned short m_byTotalLen;	        //16总长度
	unsigned short m_usID;			        //16标识
	unsigned short m_usFlagFragOffset;		//3位标识+13位片偏移
	unsigned char m_byTTL;					//8TTL
	unsigned char byProtocol;				//8位协议
	unsigned short m_usHChecksum;			//16位首部检验和
	unsigned int m_ulSrcIP;				    //32源IP地址
	unsigned int m_ulDestIP;				//32目的IP地址
};
//ICMP数据包头结构   8字节
struct ICMPHeader
{
	unsigned char m_byType;					//类型
	unsigned char m_byCode;					//代码
	unsigned short m_usChecksum;			//检验和
	unsigned short m_usID;					//标识符
	unsigned short m_usSeq;					//序号
};

//计算校验和
unsigned short CalCheckSum(unsigned short *pBuffer, int nSize)
{
	unsigned long ulCheckSum = 0;
	while (nSize > 1)
	{
		ulCheckSum += *pBuffer++;
		nSize -= sizeof(unsigned short);
	}
	if (nSize)
	{
		ulCheckSum += *(unsigned short *)pBuffer;
	}

	ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);
	ulCheckSum += (ulCheckSum >> 16);
	return (unsigned short)(~ulCheckSum);
}
ICMPHeader *pICMPHeader = NULL;
//填充数据包
void fillICMPHeader(char *icmpData,int dataSize)
{
	pICMPHeader = (ICMPHeader*)icmpData;
	pICMPHeader->m_byType = 8;
	pICMPHeader->m_byCode = 0;
	pICMPHeader->m_usID = (unsigned short)GetCurrentThreadId();    
	pICMPHeader->m_usSeq = 0;
	pICMPHeader->m_usChecksum = CalCheckSum((unsigned short *)icmpData, dataSize);
}

//解析
int  resolutionResponse(char* recvbuf, int bytes, sockaddr_in *recv, int tid)
{
	IPHeader *ipHead = (IPHeader *)recvbuf;
	unsigned short ipHeadLen = ((ipHead->m_VerHen & 0x0f) * 4);
	if (bytes<ipHeadLen + 8)     //ICMP数据不完整
	{
		return -1;
	}
	ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + ipHeadLen);
	if (pICMPHeader->m_byType!=0)//0表示存在回显应答
	{
		return -2;

	}
	if (pICMPHeader->m_usID != (unsigned short)tid)
	{
		return -3;
	}

	return 0;   //正确
}

int ping(DWORD dwDestIP, unsigned int timeout)
{
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
	{                                       //如果初始不成功则报错,GetLastError()返回发生的错误消息
		cout << "WSAStarup() failed :" << GetLastError << endl;
		return -5;
	}
	SOCKET m_sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);   

	if (m_sockRaw == INVALID_SOCKET)
	{
		cerr << "WSASocket() failed :" << WSAGetLastError() << "eeee" << endl;
		return -6;
	}
	setsockopt(m_sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));//接收超时选项
	setsockopt(m_sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));//发送超时选项

	//准备发送的数据
	
	char icmpData[40] = { 0 };

	char data[32] = {0};//ICMP数据部分
	memcpy(data, "abcdefghijklmnopqrstuvwabcdefghi", 32);
	memcpy(icmpData + sizeof(ICMPHeader), &data, 32);//在ICMP包头后,添加数据部分
	int dataSize = sizeof(ICMPHeader) + sizeof(data);
	fillICMPHeader(icmpData, dataSize);

	
	//远程通信
	sockaddr_in sockaddrDest;//目标地址
	memset(&sockaddrDest, 0, sizeof(sockaddrDest));
	sockaddrDest.sin_family = AF_INET;
	sockaddrDest.sin_addr.S_un.S_addr = dwDestIP;

	//发送数据
	sendto(m_sockRaw, icmpData, dataSize, 0, (sockaddr*)&sockaddrDest, sizeof(sockaddrDest));

	int iRet = -1;
	sockaddr_in sockaddrRecv;//收到的反馈
	int recvLen = sizeof(sockaddrRecv);
	while (1)
	{
		char recvBuf[1024] = { 0 };
		int iRecv = recvfrom(m_sockRaw, recvBuf, 1024, 0, (sockaddr*)&sockaddrRecv, &recvLen);

		int ret = resolutionResponse(recvBuf, iRecv, &sockaddrRecv, GetCurrentThreadId());

		if (ret == 0)
		{
			iRet = 0;
			cout << "在线" << endl;
			break;
		}
		else if (ret==-2)
		{
			iRet = -1;
			cout << "离线" << endl;
			break;
		}
		else
		{
			iRet = -2;
			cout << "超时" << endl;
			break;
		}
	}

	//释放套接字
	closesocket(m_sockRaw);
	WSACleanup();

	return iRet;
}

int main()
{
	
	string ip = "192.168.1.66";
	int ret = ping(inet_addr(ip.c_str()), 3000);
	system("pause");
	return 0;
}

结果:在线

WireShark抓包结果如下
C++实现基于ICMP协议的ping命令_第2张图片

你可能感兴趣的:(网络,c++,udp,开发语言)