Ping的基本原理是依赖ICMP协议。ICMP包头如下:
类型和code定义如下:
设计思想
- 定义IP头部结构
- 定义ICMP头部结构
- 构造ICPM报文并且计算ICMP的校验和
- 通过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);