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

记不起在哪个项目中需要做一个检查设备在线状态的功能,可当时又没有设备的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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当然,我很乐意回答您的问题。以下是一个可以实现ping多个网络设备的Python代码示例: ```python import os def ping(ip): """ Ping 给定 IP,检查是否可以到达 """ response = os.system("ping -n 1 " + ip) return response == 0 if __name__ == '__main__': devices = [ '192.168.1.1', '192.168.1.2', '192.168.1.3', ] for device in devices: if ping(device): print(f'{device} is up!') else: print(f'{device} is down!') ``` 在这个例子,我们定义了一个`ping()`函数,它接受一个IP地址并返回True或False,表示是否能够到达该设备。然后我们定义了一个包含多个设备IP的列表,并使用一个循环来遍历它们并执行ping操作。如果ping成功,则输出“{设备} is up!”,否则输出“{设备} is down!”。 希望这个示例可以帮助您! ### 回答2: 以下是一个使用Python编写的ping多个网络设备的代码示例: ```python import subprocess def ping_device(device_ip): # 使用subprocess模块执行ping命令 p = subprocess.Popen(['ping', '-c', '4', device_ip], stdout=subprocess.PIPE) # 获取ping命令的输出结果 output, _ = p.communicate() # 检查ping命令的返回值 if p.returncode == 0: result = f"{device_ip} is reachable." else: result = f"{device_ip} is unreachable." return result def ping_multiple_devices(device_ips): results = [] for device_ip in device_ips: result = ping_device(device_ip) results.append(result) return results # 要ping设备IP列表 device_ips = ['192.168.0.1', '192.168.0.2', '192.168.0.3'] # 调用ping_multiple_devices函数并打印结果 ping_results = ping_multiple_devices(device_ips) for result in ping_results: print(result) ``` 在上述代码,我们首先定义了一个`ping_device`函数,用于ping单个设备。使用`subprocess.Popen`执行ping命令,并使用`communicate`方法获取ping命令的输出结果。根据ping命令的返回值(0表示成功,非0表示失败),我们可以判断设备是否可达,并返回相应结果。 然后,我们定义了`ping_multiple_devices`函数,该函数接收设备IP列表,并依次调用`ping_device`函数进行ping操作。最终,将每个设备ping结果存储在一个列表,并返回该列表。 最后,在主程序定义了要ping设备IP列表,并调用`ping_multiple_devices`函数进行ping操作。将结果打印出来。 这样,我们就可以使用这段代码快速、便捷地进行多个网络设备ping操作。 ### 回答3: 下面是一个简单的Python代码示例,可以使用ping命令来批量ping多个网络设备。 ```python import os def ping_devices(devices): for device in devices: response = os.system("ping -c 1 " + device) if response == 0: print(device + " is online") else: print(device + " is offline") # 定义需要ping设备IP地址列表 devices = ["192.168.1.1", "192.168.1.2", "192.168.1.3"] # 调用ping_devices函数 ping_devices(devices) ``` 上述代码,`ping_devices`函数接受一个设备IP地址列表作为参数,并使用`os.system`函数执行ping命令。如果ping命令返回值为0,表示设备在线;如果返回值为非零,则表示设备离线。根据返回值的不同,函数会打印出相应的设备在线或者离线的信息。 你可以根据自己的需求修改`devices`列表IP地址,添加或移除需要ping设备
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值