#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
typedef struct _IPHeader // 20字节的IP头
{
UCHAR iphVerLen; // 版本号和头长度(各占4位)
UCHAR ipTOS; // 服务类型
USHORT ipLength; // 封包总长度,即整个IP报的长度
USHORT ipID; // 封包标识,惟一标识发送的每一个数据报
USHORT ipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是TTL
UCHAR ipProtocol; // 协议,可能是TCP、UDP、ICMP等
USHORT ipChecksum; // 校验和
ULONG ipSource; // 源IP地址
ULONG ipDestination; // 目标IP地址
} IPHeader, *PIPHeader;
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;
//////////////////////////////////////////////////////////////////////////////////
// 校验和的计算
// 以16位的字为单位将缓冲区的内容相加,如果缓冲区长度为奇数,
// 则再加上一个字节。它们的和存入一个32位的双字中
USHORT checksum(USHORT * buff, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{ // 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
WSACleanup();
}
};
CInitSock theSock;//全局类,开始于main之前,结束于main之后。
/////////////////////////////////////////////////////////////////////////////////////
int main()
{
SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);// 创建原始套节字
int x = 100;// 设置接收超时
setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&x, sizeof(x));
SOCKADDR_IN dest;// 设置目的地址
dest.sin_family = AF_INET;
dest.sin_port = htons(0);
dest.sin_addr.S_un.S_addr = inet_addr("202.102.75.147");// 要Ping的IP地址 202.102.75.147
char buff[sizeof(ICMP_HDR) /*+ 32*/] = {0}; //此处的32和下面的填充用的32,这两个数要一样。大小不超过65536 USHORT
ICMP_HDR * pIcmp = (ICMP_HDR*)buff;
pIcmp->icmp_type = 8;// 请求一个ICMP回显
pIcmp->icmp_id = (USHORT)GetCurrentProcessId();//id不可以为0.
//memset(&buff[sizeof(ICMP_HDR)], 'E', 32);// 填充数据部分,可以为任意
for (int i = 0;i<4;i++)
{
pIcmp->icmp_checksum = 0; //此行注视掉,只有第一个成功。所以不可以注视掉。
pIcmp->icmp_timestamp = GetTickCount();
pIcmp->icmp_sequence = i;
pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) /*+ 32*/);//此处的32和下面的32加上,是发送64字节的。
sendto(sRaw, buff, sizeof(ICMP_HDR) /*+ 32*/ , 0, (SOCKADDR *)&dest, sizeof(dest));//发送ICMP封包
char recvBuf[1024];//接收ICMP封包
SOCKADDR_IN from;
int nLen = sizeof(from);
int nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
if(nRet == SOCKET_ERROR)
{//处理失败。
if(WSAGetLastError() == WSAETIMEDOUT)
{
printf(" timed out\n");
continue;
}
printf(" recvfrom() failed: %d\n", ::WSAGetLastError());
return -1;
}
printf("Reply from %s: bytes=%d", inet_ntoa(from.sin_addr), nRet);//显示信息。
int nTick = GetTickCount();
ICMP_HDR * pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // 接收到的数据中包含IP头,所以加20得到ICMP头 sizeof(IPHeader) == 0,
printf(" time=%dms", nTick - pRecvIcmp->icmp_timestamp); //等于零就是小于1.
IPHeader * ih = (IPHeader *)recvBuf;
printf(" TTL=%d\n", ih->ipTTL);
Sleep(100);
}
return 0;
}
//made at 2011.12.27
//还有添加路由等功能的,没有做。
没有评论:
发表评论