收藏一个Ping的小工具类,可用于网络中检测目的设备是否在线

来源:互联网 发布:unity3d制作下雨特效 编辑:程序博客网 时间:2024/05/30 04:22

记不起在哪个项目中需要做一个检查设备在线状态的功能,可当时又没有设备的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;}


0 0
原创粉丝点击