// ping.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#pragma comment(lib,"ws2_32")
//定义ICMP首部
typedef struct icmp_hdr{
unsigned char icmp_type; //ICMP消息类型,回显请求:8,回显应答请求:0
unsigned char icmp_code; //代码,代码域进一步定义了请求或消息的类型
unsigned short icmp_checksum;//校验和
//下面是回显头
unsigned short icmp_id; //ICMP数据报的ID号;用来唯一标识此请求的ID号,通常设为进程ID
unsigned short icmp_sequence;//ICMP数据报的序列号
unsigned long icmp_timestamp;//时间戳,可选数据部分,可以忽略
}ICMP_HDR; ///
struct IpHeader {
BYTE HeaderLength; //首部长度
BYTE Version; //版本
BYTE DS; //区分服务
WORD TotalLength; //总长度
WORD ID; //标识
BYTE FragmentOffset0; //片偏移
BYTE MF; //MF标识
BYTE DF; //DF标识
BYTE Reserved; //保留标识
BYTE FragmentOffset1; //片偏移
BYTE TTL; //生存时间
BYTE Protocol; //协议
WORD Checksum; //检验和
DWORD SourceAddress; //源地址
DWORD DestinationAddress;//目的地址
};
USHORT checksum(USHORT* buffer, int size) //计算校验和
{
ULONG cksum = 0;
while (size > 1) { //将数据以字为单位累加到cksum中
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size) { //如果数据长度为奇数,最后一个字节将被扩展到字,累加的结果是一个双字
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xFFFF);//将双字的高16位于低16位相加
cksum += (cksum >> 16);
return (USHORT)(~cksum); //最后取反得到校验和
}
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
int err=WSAStartup(wVersionRequested,&wsaData);
if (err != 0) return 0;
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1){
WSACleanup();
return 0;
}
char* szDestIp = "127.0.0.1"; //目标IP地址,即要Ping的IP地址
SOCKET sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //创建原始套接字
SOCKADDR_IN dest; //设置目的地址
dest.sin_family = AF_INET;
dest.sin_addr.S_un.S_addr = inet_addr(szDestIp);
dest.sin_port = htons(0);
char buff[sizeof(ICMP_HDR)+32]; //创建ICMP封包
ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
//下面填写ICMP封包数据
pIcmp->icmp_type = 8;//请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_checksum = 0;
pIcmp->icmp_id = (USHORT)GetCurrentProcessId();//GetCurrentProcessId:获取当前进程的一个唯一标识符
pIcmp->icmp_sequence = 0;
memset(buff, 'E', 32);//填充数据部分,可以为任意
int timeout = 1000; //设置接收超时处理
int _break = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (_break == SOCKET_ERROR){
fprintf(stderr, "failed to set timeout:%d\n", WSAGetLastError());
}
if (bind(sockRaw, (sockaddr*)&dest, sizeof(dest)) == SOCKET_ERROR) {
printf("bind error!\n");
return 0;
}
//开始发送和接收ICMP封包
USHORT Seq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int Len = sizeof(from);
while (1) {
static int Count = 0;
int Ret;
if (Count++ == 4) break;
pIcmp->icmp_checksum = 0;
pIcmp->icmp_timestamp = GetTickCount();
pIcmp->icmp_sequence = Seq++;
pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32);
Ret = sendto(sockRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR*)&dest, sizeof(dest));
if (Ret == SOCKET_ERROR) {
printf("sendto() failed:%d\n", WSAGetLastError);
return -1;
}
Ret = recvfrom(sockRaw, recvBuf, 1024, 0, (SOCKADDR*)&from, &Len);
if (Ret == SOCKET_ERROR) {
if (WSAGetLastError() == WSAETIMEDOUT) {
printf("timed out\n");
continue;
}
printf("recvfrom() failed:%d\n", WSAGetLastError());
return -1;
}
//开始解析接收到的ICMP
int Tick = ::GetTickCount();
if (Ret < sizeof(IpHeader) + sizeof(ICMP_HDR))
printf("too few bytes from %s\n", inet_ntoa(from.sin_addr));
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + sizeof(IpHeader));
if (pRecvIcmp->icmp_type != 0) {//回显
printf("nonecho type %d recvd\n", pRecvIcmp->icmp_type);
return -1;
}
if (pRecvIcmp->icmp_id != GetCurrentProcessId()) {
printf("someone else's packet!\n");
return -1;
}
printf("%d bytes from %s:", Ret, inet_ntoa(from.sin_addr));
printf("icmp_seq = %d , ", pRecvIcmp->icmp_sequence);
printf("time: %d ms\n", Tick - pRecvIcmp->icmp_timestamp);
Sleep(1000);
}
return 0;
}