ICMP英文解释:Internet Control Message Protocol
ICMP常见类型:
ICMP类型和编码:
icmp request:
icmp reply:
icmp协议实现ping程序的测试代码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#pragma comment(lib, "WS2_32")
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if (::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
#include <iostream>
using namespace std;
CInitSock initSock; // 初始化Winsock库
#pragma pack(1)
typedef struct icmp_hdr
{
unsigned char icmp_type; // 消息类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
// 下面是回显头
unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID
unsigned short icmp_sequence; // 序列号
//unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, * PICMP_HDR;
typedef struct
{
unsigned char pad[20];
}IPHeader;
#pragma pack()
USHORT checksum(USHORT* buff, int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buff;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
int main()
{
// 目的IP地址,即要Ping的IP地址
char szDestIp[] = "36.152.44.96"; //江苏省南京市 北京百度网讯科技有限公司移动节点
// 创建原始套节字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
// 设置目的地址
SOCKADDR_IN dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(12345); //can be any thing,forget it, just test it!
dest.sin_addr.S_un.S_addr = inet_addr(szDestIp);
// 创建ICMP封包
char buff[sizeof(ICMP_HDR) + 32];
ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
// 填写ICMP封包数据
pIcmp->icmp_type = 8; // 请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (USHORT)::GetCurrentProcessId();
pIcmp->icmp_checksum = 0;
pIcmp->icmp_sequence = 0;
// 填充数据部分,可以为任意
memset(&buff[sizeof(ICMP_HDR)], 'E', 32);
// 开始发送和接收ICMP封包
USHORT nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
while (TRUE)
{
static int nCount = 0;
int nRet;
if (nCount++ == 4) {
break;
}
pIcmp->icmp_checksum = 0;
//pIcmp->icmp_timestamp = ::GetTickCount();
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32);
nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR*)&dest, sizeof(dest));
if (nRet == SOCKET_ERROR)
{
cout << " sendto() failed: " << ::WSAGetLastError() << endl;
return -1;
}
nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
if (nRet == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAETIMEDOUT)
{
cout << " timed out" << endl;
continue;
}
cout << " recvfrom() failed: " << ::WSAGetLastError() << endl;
return -1;
}
// 下面开始解析接收到的ICMP封包
int nTick = ::GetTickCount();
if (nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))
{
cout << " Too few bytes from %s" << ::inet_ntoa(from.sin_addr) << endl;
}
// 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
if (pRecvIcmp->icmp_type != 0) // 回显
{
cout << " nonecho type " << pRecvIcmp->icmp_type << " recvd" << endl;
return -1;
}
if (pRecvIcmp->icmp_id != ::GetCurrentProcessId())
{
cout << " someone else's packet!" << endl;
return -1;
}
cout << nRet << " bytes from " << inet_ntoa(from.sin_addr)
<< " icmp_seq = " << pRecvIcmp->icmp_sequence << "."
<< " time: " << nTick /*- pRecvIcmp->icmp_timestamp*/ << "ms" << endl;
::Sleep(1000);
}
return 0;
}