记不起在哪个项目中需要做一个检查设备在线状态的功能,可当时又没有设备的SDK可使用,网上找索很久,找到这款PING的小工具,简单适用,自己修改了一下,用起来超方便。 自己先攒起来再说。
[头文件]
#ifndef _PINGTOOL_H
#define _PINGTOOL_H
#include <winsock2.h>
#include <iostream>
#pragma comment( lib, "ws2_32.lib" )
class CPintTool
{
#define ICMP_ECHO 8 //回应请求类型的ICMP消息类型
#define ICMP_ECHOREPLY 0 //回应答复型的ICMP消息类型
#define ICMP_MIN 8 // ICMP数据报最小长度
#define DEF_PACKET_SIZE 32 //默认数据报大小
#define DEF_PACKET_NUMBER 3 //发送数据报的个数
#define MAX_PACKET 1024 //最大ICMP数据报大小
public:
CPintTool();
~CPintTool();
public:
bool pintTo( char* destIP );
private:
SOCKET sockRaw;
int destlen, fromlen, bwrote, bread;
sockaddr_in dest,from;
char *icmp_data, *recvbuf;
struct hostent *hp;
unsigned short seq;
//成员变量的定义
char *Ipdest; //目标主机的IP地址
int datasize; //ICMP消息的长度
//接收到的ICMP消息数,每收到一条ICMP消息就加1
// static int icmpcount=0;
int icmpcount;
//IP头(20字节)
struct IPHEAD
{
unsigned int headlength:4; // 4位头长度
unsigned int version:4; // 4位版本
unsigned char tos; // 8位服务类型
unsigned short totallength; // 16位总长
unsigned short ip_id; // 16位标识
unsigned short frag_and_flags; //3个一位标识加分段偏移
unsigned char ttl; //8位存活时间
unsigned char protocal; //8位协议类型
unsigned short ip_checksum; //16位头校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目标IP地址
};
// ICMP 头(12字节)
struct ICMPHEAD
{
unsigned char type; //类型(0,8)
unsigned char code; //代码(0)
unsigned short icmp_checksum; //校验和
unsigned short icmp_id; //标识符
unsigned short seq; //序号
unsigned long timestamp; //时间戳
};
private:
//成员函数的定义
//Fill_icmp_data函数:用于设置ICMP头部,填充ICMP消息
void Fill_icmp_data(char * icmp_data, int datasize);
//DecodeICMPHEAD函数:接受数据时从IP头中获取ICMP消息
void DecodeICMPHEAD(char *recvbuf, int bread, sockaddr_in *from);
//Checksum函数:用于求ICMP首部校验和
unsigned short Checksum(unsigned short *buffer, int size);
//Usage函数:表示程序的功能
void Usage(char *progname);
};
#endif //_PINGTOOL_H
[CPP文件]
#include <stdafx.h>
#include <iostream>
#include "PingTool.h"
using namespace std;
CPintTool::CPintTool()
:icmp_data( NULL )
,recvbuf( NULL )
,hp( NULL )
,seq( 0 )
,sockRaw( INVALID_SOCKET )
,datasize( DEF_PACKET_SIZE ) //数据包大小为DEF_PACKET_SIZE=32
{
WSADATA wsaData;
//启动winsock失败,输出错误提示信息
if (WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
{
cout<<"WSAStartup failed:"<<GetLastError();
cout<<endl;
return;
}
destlen = sizeof(sockaddr_in);
fromlen = sizeof(sockaddr_in);
}
CPintTool::~CPintTool()
{
WSACleanup();
}
bool CPintTool::pintTo( char* destIP )
{
Ipdest= destIP; //默认目的地址为:202.119.81.120
//创建套接字失败,输出错误信息提示
sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,
NULL, 0,WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
cout<<"WSASocket() failed:"<<GetLastError();
cout<<endl;
return false;
}
//设定发送数据时最长等待时间timeout=1000ms
int timeout=1000;
bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,
(char *)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR)
{
cout<<"setsockopt(SO_RCVTIMEO) failed:"<<WSAGetLastError();
cout<<endl;
return false;
}
//设定接收数据时最长等待时间timeout=1000ms
timeout=1000;
bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,
(char *)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR)
{
cout<<"setsockopt(SO_SNDTIMEO) failed:"<<WSAGetLastError();
cout<<endl;
return false;
}
//解析目的地址的名称
dest.sin_family=AF_INET;
if(dest.sin_addr.s_addr=inet_addr(Ipdest))
{
//inet_addr将IP地址转换成网络地址
if((hp=gethostbyname(Ipdest))!=NULL)
{
//gethostbyname主机信息
memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length); //复制内存
dest.sin_family=hp->h_addrtype;
cout<<"dest.sin_addr="<<inet_ntoa(dest.sin_addr);
cout<<endl<<'\n';
}
else
{
cout<<"gethostbyname() failed:"<<WSAGetLastError();
cout<<endl;
return false;
}
}
//建立一个ICMP数据包
datasize+=sizeof(ICMPHEAD);
//为要发送的数据申请内存空间,最大值为1024
icmp_data=new char[MAX_PACKET];
//为要接收的数据申请内存空间,最大值为1024
recvbuf=new char[MAX_PACKET];
//分配内存失败,输出提示信息
if(!icmp_data)
{
cout<<"new char[] failed:"<<WSAGetLastError();
cout<<endl;
return false;
}
//将ICMP信息所在空间清零
memset(icmp_data,0,MAX_PACKET);
//设置ICMP信息头部
Fill_icmp_data(icmp_data,datasize);
//发送和接收数据,循环三次
icmpcount = 0;
for(int i=0;i<3;i++)
{
//ICMP信息封装
((ICMPHEAD*)icmp_data)->icmp_checksum=0;
((ICMPHEAD*)icmp_data)->timestamp = GetTickCount();
((ICMPHEAD*)icmp_data)->seq=seq++;
((ICMPHEAD*)icmp_data)->icmp_checksum =
Checksum((unsigned short*)icmp_data,datasize);
//发送数据
bwrote = sendto(sockRaw,icmp_data,datasize,0,
(sockaddr*)&dest,destlen);
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout<<"Request timed out."<<endl;
continue;
}
else
{
cout<<"sendto( ) failed:"<<WSAGetLastError();
cout<<endl;
return false;
}
}
//显示实际发送的字节数
cout<<"Pinging "<<inet_ntoa(dest.sin_addr)<<" with ";
cout<<bwrote<<" bytes of data:";
cout<<endl;
//接收数据
bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,
(sockaddr*)&from,&fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout<<"Request timed out."<<endl;
continue;
}
cout<<"recvfrom() failed: "<<WSAGetLastError();
cout<<endl;
return false;
}
//打开接收到的数据,从中提取ICMP头信息
DecodeICMPHEAD(recvbuf,bread,&from);
//等待1s钟
Sleep(1000);
}
//输出发送的数据包数、接收的数据包数和丢失的数据包数
cout<<endl;
cout<<"Ping statistics for "<<inet_ntoa(dest.sin_addr)<<":"<<endl;
cout<<" Packets:sent=4,";
cout<<"Received="<<icmpcount;
cout<<",Lost="<<DEF_PACKET_NUMBER-icmpcount;
cout<<"("<<((float)(DEF_PACKET_NUMBER-icmpcount)/DEF_PACKET_NUMBER)*100;
cout<<"%loss)";
cout<<endl;
//释放资源,关闭winsock
if(sockRaw!=INVALID_SOCKET)
closesocket(sockRaw);
delete[] recvbuf;
delete[] icmp_data;
if ( icmpcount == 0 )
return false;
return true;
}
//Fill_icmp_data函数:用于设置ICMP头部,填充ICMP消息
void CPintTool::Fill_icmp_data(char * icmp_data, int datasize)
{
ICMPHEAD *icmphead=NULL;
char *datapart=NULL;
icmphead = (ICMPHEAD*)icmp_data;
icmphead->type = ICMP_ECHO; //ICMP_ECHO=8
icmphead->code = 0; //类型为8,代码为0,表示回应请求
icmphead->icmp_checksum = 0;
icmphead->icmp_id = (unsigned short)GetCurrentProcessId();
datapart = icmp_data + sizeof(ICMPHEAD);//具体内容的首地址指针
//初始化数据包内容部分
for( int i=0;i < (datasize-sizeof(ICMPHEAD)); i++,datapart++ )
*datapart=0;
}
//DecodeICMPHEAD函数:接受数据时从IP头中获取ICMP消息
void CPintTool::DecodeICMPHEAD(char *recvbuf, int bread, sockaddr_in *from)
{
IPHEAD *iphead=NULL;
ICMPHEAD *icmphead=NULL;
DWORD tick;
unsigned short iphdrlen;
iphead = (IPHEAD *)recvbuf;
//32位字的个数乘以4即字节个数
iphdrlen = (iphead->headlength) * 4 ;
//获取操作系统启动至今所经过的时间(ms)
tick=GetTickCount();
if (bread < (iphdrlen + ICMP_MIN))
{
cout<<"Too few bytes from: "<<inet_ntoa(from->sin_addr);
cout<<endl;
}
icmphead = (ICMPHEAD*)(recvbuf + iphdrlen); //ICMP消息始于IP头之后
//若ICMP消息类型并非查询,则输出相应信息类型
if (icmphead->type != ICMP_ECHOREPLY)
{
cout<<"nonecho type "<<int(icmphead->type)<<" received";
cout<<endl;
}
//确认收到的应答ICMP消息是否是对发送出去的消息的回应
//若其表示与当前进程不符,则输出出错信息
if (icmphead->icmp_id != (unsigned short)GetCurrentProcessId())
{
cout<<"It's someone else's packet!"<<endl;
return;
}
//输出收到信息的内容:主机地址,icmp消息序号,回应时间,存活时间
cout<<"Reply from "<<inet_ntoa(from->sin_addr);
cout<<" bytes="<<bread-iphdrlen;
cout<<" time: "<<GetTickCount()-icmphead->timestamp<<" ms ";
cout<<" seq="<<icmphead->seq;
cout<<endl;
icmpcount++;
}
//Checksum函数:用于求ICMP首部校验和
unsigned short CPintTool::Checksum(unsigned short *buffer, int size)
{
unsigned long cksum=0; //设校验和初值为0
while(size >1)
{
cksum+=*buffer++; //求各个16位数字之和
size -=sizeof(unsigned short);
}
if(size)
{
cksum += *(unsigned char*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff); //高位与低位相加
cksum += (cksum >>16); //加上进位
return (unsigned short)(~cksum); //取反得到校验和
}
//Usage函数:表示程序的功能
void CPintTool::Usage(char *progname)
{
cout<<"Usage:"<<progname<<" <host>"<<endl;
cout<<" host remote machine to Ping"<<endl;
cout<<endl;
}
[用法:]
#include "stdafx.h"
#include <iostream>
#include "PingTool.h"
int _tmain(int argc, _TCHAR* argv[])
{
CPintTool pingT;
char* ipdest = "192.168.3.254";
bool boo = pingT.pintTo( ipdest );
if ( boo )
{
<span style="white-space:pre"> </span>std::cout << " 目标地址可达! " << std::endl;
}
else
{
<span style="white-space:pre"> </span>std::cout << " 目标地址不可达!!! " << std::endl;
}
return 0;
}