Windows下基于Qt用c++实现ping

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/junbujianwpl/article/details/76383629

Windows下基于Qt用c++实现ping

1、代码来源

此份ping的源码来自于vc6对应msdn中。自己稍做修改,然后成了现在这个版本。


2、winsock与winsock2

windows下socket有2个版本,分别对应2个不同的头文件 winsock.h 和 winsock2.h 。对应WSAStartup第一个参数是MAKEWORD(1,1)还是MAKEWORD(1,2)。

这俩有啥不同呢。。。。还没具体研究过。。。。不过个人感觉winsock更通用,因为winsock2里面socket都可以用WSASocket来创建。

而偶个人感觉,用socket s=socket(AF_NET…….)的方式创建更通用。所以很执拗的使用winsock版本,抛弃winsock2。因为socket是POSIX通用的接口。用这个,跨平台移植工作量更小。

然后,winsock2是基本前向兼容winsock的。为啥说基本,因为偶没具体深入研究证实过。。。我所知道的其中一个证据是winsock与winsock2同处于一个dll文件中,WS2_32.dll。但是通过不同的头文件可以对应不同的版本。或者说winsock.h是为了保证原有代码可用的一个措施。。。。。。。。


3、Qt工程文件配置

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp \
    Pinger.cpp

HEADERS += \
    Pinger.h

#Qt下导入winsock。2个版本的都在这里面了。
LIBS += -lWS2_32

4、类头文件

#ifndef PINGER_H
#define PINGER_H

#include <windows.h>
#include <winsock.h>
#include <string>

#pragma pack(push)
#pragma pack(1)

struct IpHead{
    unsigned int    uiHeadLen:4;            ///<头部长度
    unsigned int    uiVersion:4;            ///<版本
    unsigned char   ucTos;                  ///<服务类型,type of service
    unsigned short  usTotalLen;             ///<总长度
    unsigned short  usIpId;                 ///<标识
    unsigned short  usFlags;                ///<3位标志+13位片偏移
    unsigned char   ucTtl;                  ///<TTL,time to live
    unsigned char   ucProtocol;             ///<协议
    unsigned short  usCheckSum;             ///<首部总校验和
    unsigned int    uiSrcIP;                ///<源ip
    unsigned int    uiDstIP;                ///<目的ip
};

struct IcmpHead{
    unsigned char   ucType;                 ///<类型
    unsigned char   ucCode;                 ///<代码
    unsigned short  ususIcmpChkSum;         ///<校验和
    unsigned short  usIcmpId;               ///<id
    unsigned char   usSeq;                  ///<序号
    unsigned long   ulTimeStamp;            ///<时间戳
};

#pragma pack(pop)


#define DEF_PACKET_SIZE     32
#define ECHO_REQUEST        8
#define ECHO_REPLY          0
#define ICMP_ECHOREPLY      0
#define ICMP_MIN            sizeof(IcmpHead)
#define ICMP_ECHO           8

class Pinger
{
public:
    Pinger();
    virtual ~Pinger();

    /**
     * @brief ping          ping指定ip地址
     * @param dstIP         目的ip,不能包含空格等非法字符
     * @param packNum       一共ping几包
     * @param sndTime       发送超时时间,单位毫秒
     * @param rcvTime       接收超时时间,单位毫秒
     * @return              成功ping通的包数,大于0表示ping通
     */
    int ping(const char* dstIP, const int& packNum, const int &sndTime, const int &rcvTime);

    /**
     * @brief getTips       获取提示信息
     * @return              提示信息
     */
    std::string getTips() const {return m_strTips_;}

protected:

    /**
     * @brief checkSum      计算校验和
     * @param buf           待计算缓冲区
     * @param wordCnt       字个数
     * @return              校验和
     */
    unsigned short checkSum(const WORD *buf, const int &wordCnt);

    /**
     * @brief decodeIcmpHead        解析icmp头
     * @param rcvBuf                头部缓冲区
     * @param bread                 字节数
     * @param from                  来源ip地址
     * @return                      0表示正常,其他见错误码EnErrCode
     */
    int decodeIcmpHead(char *rcvBuf,unsigned int bread,sockaddr_in *from);

    /**
     * @brief fillImcpData          填充icmp数据
     * @param icmpData              缓冲区
     * @param byteCnt               缓冲区长度
     */
    void fillImcpData(char *icmpData, int byteCnt);

    std::string  m_strTips_;                ///<提示信息

private:
    enum EnErrCode{
        EnOK,
        EnNullPtr,
        EnBadData,
        EnInvalidIp,
        EnSockErr,
    };

    unsigned int m_uiId__;                  ///<当前对象id计数

    static unsigned int m_uiCnt__;          ///<总对象创建数计数
};

#endif // PINGER_H

5、类源文件

#include "Pinger.h"

unsigned int Pinger::m_uiCnt__=0;

Pinger::Pinger()
{
    WSADATA data;
    char tips[256]={0};
    snprintf(tips,sizeof(tips),"wsa init ret %d,errCode:%d.\n",WSAStartup(MAKEWORD(1,2),&data),WSAGetLastError());
    m_strTips_+=tips;
    m_uiId__=m_uiCnt__++;
}

Pinger::~Pinger()
{
    WSACleanup();
}

int Pinger::ping(const char *dstIP, const int& packNum, const int& sndTime, const int& rcvTime)
{
    int nRet=0;
    m_strTips_+="ping tips.\n";
    char tips[256]={0};
    SOCKET sockRaw=INVALID_SOCKET;
    struct sockaddr_in  dest,from;
    unsigned short seq_no=0;

    int bread,datasize;
    int fromlen=sizeof(from);
    char icmp_data[1024]={0};
    char rcvbuf[1024]={0};
    unsigned int addr = inet_addr(dstIP);
    int timeout=sndTime;

    if(INADDR_NONE ==addr){
        m_strTips_+="invalid dstip,";
        m_strTips_+=dstIP;
        return EnInvalidIp;
    }

    sockRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
    if(INVALID_SOCKET == sockRaw){
        snprintf(tips,sizeof(tips),"create sock failed,errcode:%d\n",WSAGetLastError());
        m_strTips_+=tips;
        return EnSockErr;
    }

    //set send time-out val
    bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));
    snprintf(tips,sizeof(tips),"set send time-out %d,ret:%d,errCode:%d.",timeout,bread,WSAGetLastError());
    m_strTips_+=tips;

    //set recv time-out val
    timeout=rcvTime;
    bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
    snprintf(tips,sizeof(tips),"set recv time-out %d,ret:%d,errCode:%d.",timeout,bread,WSAGetLastError());
    m_strTips_+=tips;

    memset(&dest,0,sizeof(dest));
    dest.sin_addr.s_addr=addr;
    dest.sin_family= AF_INET;
    datasize=DEF_PACKET_SIZE+sizeof(IcmpHead);

    m_strTips_+="\n";
    for(int i=packNum;i>0;i--){
        fillIcmpData(icmp_data,datasize);
        ((IcmpHead*)icmp_data)->ulTimeStamp=GetTickCount();
        ((IcmpHead*)icmp_data)->usSeq=seq_no++;
        ((IcmpHead*)icmp_data)->ususIcmpChkSum=checkSum((WORD*)icmp_data,datasize);

        int bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
        snprintf(tips,sizeof(tips),"send,ret:%d,sndErrCode:%d,",bwrote,WSAGetLastError());
        m_strTips_+=tips;

        bread = recvfrom(sockRaw,rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&from,&fromlen);
        snprintf(tips,sizeof(tips),"recv,ret:%d,rcvErrCode:%d.",bread,WSAGetLastError());
        m_strTips_+=tips;

        if(bread>0){
            decodeIcmpHead(rcvbuf,bread,&from);
            nRet++;
        }
        Sleep(200);
        m_strTips_+="\n";
    }

    if(INVALID_SOCKET != sockRaw){
        int clsRet= closesocket(sockRaw);
        snprintf(tips,sizeof(tips),"close sock:%u,ret:%d,errCode:%d\n",sockRaw,clsRet,WSAGetLastError());
        m_strTips_+=tips;
    }

    return nRet;
}

unsigned short Pinger::checkSum(const WORD *buf, const int& wordCnt)
{
    WORD wChkSum=0;
    for(int i = wordCnt;i>0;i--){
        wChkSum+=*buf++;
    }
    wChkSum=(wChkSum>>16)+(wChkSum & 0xffff);
    wChkSum+=(wChkSum>>16);

    return (WORD)(~wChkSum);
}

int Pinger::decodeIcmpHead(char *rcvBuf, const unsigned int bread, const sockaddr_in *from)
{
    if(NULL == rcvBuf || NULL == from){
        m_strTips_+="decode imcp head encounter null ptr.\n";
        return EnNullPtr;
    }

    char tips[256]={0};
    IpHead *ipHead=(IpHead*)rcvBuf;
    IcmpHead *icmpHead=NULL;
    WORD  wIpHeadLen=ipHead->uiHeadLen*4;

    if(bread<(wIpHeadLen+ICMP_MIN)){
        snprintf(tips,sizeof(tips),"too few bytes from %s\n",inet_ntoa((from->sin_addr)));
        m_strTips_+=tips;
        return EnBadData;
    }

    icmpHead=(IcmpHead*)(rcvBuf+wIpHeadLen);

    if(icmpHead->ucType != ICMP_ECHOREPLY){
        snprintf(tips,sizeof(tips),"no echo tpye %d rcved.\n",int(icmpHead->ucType));
        m_strTips_+=tips;
    }

    if(icmpHead->usIcmpId != m_uiId__){
        snprintf(tips,sizeof(tips),"some one's pack. realId:%u,myId:%u.\n",icmpHead->usIcmpId,m_uiId__);
        m_strTips_+=tips;
    }

    snprintf(tips,sizeof(tips),"reply from %s, %u bytes, time:%u ms, seq:%d, id:%u.\n",inet_ntoa(from->sin_addr),
            bread-wIpHeadLen,GetTickCount()-icmpHead->ulTimeStamp,icmpHead->usSeq,icmpHead->usIcmpId);
    m_strTips_+=tips;

    return 0;
}

void Pinger::fillIcmpData(char *icmpData, const int &byteCnt)
{
    if(NULL == icmpData){
        m_strTips_+="fill icmp data encounter null ptr.\n";
        return;
    }

    IcmpHead *icmpHead=(IcmpHead*)icmpData;
    char* dataPart=NULL;
    icmpHead->ucType=ICMP_ECHO;
    icmpHead->ucCode=0;
    icmpHead->ususIcmpChkSum=0;
    icmpHead->usIcmpId=m_uiId__;
    dataPart=icmpData+sizeof(IcmpHead);
    memset(dataPart,0,byteCnt-sizeof(IcmpHead));

}

6、测试代码

#include <iostream>

#include "Pinger.h"

using namespace std;

int main()
{
    cout << "Hello World!" << endl;
    for(int i=0;i<10;i++){


        Pinger p;
        p.ping("192.168.1.1",1,200,200);
        cout<<p.getTips().c_str()<<endl;
    }
    return 0;
}

7、输出

ping了10包,有一包未回。

Hello World!
wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:15 ms, seq:0, id:0.

close sock:132,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:16 ms, seq:0, id:1.

close sock:120,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:0 ms, seq:0, id:2.

close sock:116,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:0 ms, seq:0, id:3.

close sock:112,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:0 ms, seq:0, id:4.

close sock:124,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:16 ms, seq:0, id:5.

close sock:132,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:15 ms, seq:0, id:6.

close sock:120,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:15 ms, seq:0, id:7.

close sock:116,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:-1,rcvErrCode:10060.
close sock:112,ret:0,errCode:0

wsa init ret 0,errCode:0.
ping tips.
set send time-out 200,ret:0,errCode:0.set recv time-out 200,ret:0,errCode:0.
send,ret:43,sndErrCode:0,recv,ret:63,rcvErrCode:0.reply from 192.168.1.1, 43 bytes, time:15 ms, seq:0, id:9.

close sock:124,ret:0,errCode:0

wireshark抓包截图如下:
这里写图片描述

从抓包看id设置时候高低字节没有倒一下。。。


8、git链接

此工程的git链接。
https://github.com/junbujianwpl/Pinger.git


9、编译器warning

谁能告诉偶彼样修改代码消除此2类warning。别说禁止第几号提示之类的。。。。。。

warning1:

p.ping("192.168.1.1",2,200,200);
main.cpp:11: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
     p.ping("192.168.1.1",2,200,200);
                                   ^

warning2:

sprintf(tips,"reply from %s, %u bytes, time:%u ms, seq:%d.\n",inet_ntoa(from->sin_addr),
        bread-wIpHeadLen,GetTickCount()-icmpHead->ulTimeStamp,icmpHead->usSeq);
Pinger.cpp:136: warning: format '%u' expects argument of type 'unsigned int', but argument 5 has type 'DWORD {aka long unsigned int}' [-Wformat=]
             bread-wIpHeadLen,GetTickCount()-icmpHead->ulTimeStamp,icmpHead->usSeq);
                                                                                  ^

10、git创建仓库命令

cd  pro_path
git init
git add *
git commit -m "first version"
git remote add origin your_git_uri
git fetch your_git_uri
git pull your_git_uri --allow-unrelated-histories
git push -u origin master

11、错误码

create sock failed,errcode:10013

需要管理员权限,在win7下,以管理员权限运行Qt。


12、部分错误码表

来自msdn。

code descprition
WSAEBADF 10009 File handle is not valid.The file handle supplied is not valid.
WSAEACCES 10013 Permission denied.An attempt was made to access a socket in a way forbidden by its access permissions. An example is using a broadcast address for sendto without broadcast permission being set using setsockopt(SO_BROADCAST).Another possible reason for the WSAEACCES error is that when the bind function is called (on Windows NT 4.0 with SP4 and later), another application, service, or kernel mode driver is bound to the same address with exclusive access. Such exclusive access is a new feature of Windows NT 4.0 with SP4 and later, and is implemented by using the SO_EXCLUSIVEADDRUSE option.
WSAEFAULT 10014 Bad address.The system detected an invalid pointer address in attempting to use a pointer argument of a call. This error occurs if an application passes an invalid pointer value, or if the length of the buffer is too small. For instance, if the length of an argument, which is a sockaddr structure, is smaller than the sizeof(sockaddr).
WSAEINVAL 10022 Invalid argument.Some invalid argument was supplied (for example, specifying an invalid level to the setsockopt function). In some instances, it also refers to the current state of the socket—for instance, calling accept on a socket that is not listening.
WSAEMFILE 10024 Too many open files.Too many open sockets. Each implementation may have a maximum number of socket handles available, either globally, per process, or per thread.
WSAEWOULDBLOCK 10035 Resource temporarily unavailable.This error is returned from operations on nonblocking sockets that cannot be completed immediately, for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established.
WSAEINPROGRESS 10036 Operation now in progress.A blocking operation is currently executing. Windows Sockets only allows a single blocking operation—per- task or thread—to be outstanding, and if any other function call is made (whether or not it references that or any other socket) the function fails with the WSAEINPROGRESS error.
展开阅读全文

没有更多推荐了,返回首页