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

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。

codedescprition
WSAEBADF 10009File handle is not valid.The file handle supplied is not valid.
WSAEACCES 10013Permission 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 10014Bad 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 10022Invalid 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 10024Too 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 10035Resource 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 10036Operation 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.
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
基于qt使用c++实现图书管理系统源码,需要考虑如何实现图书的添加、删除、修改、查询等功能。首先,需要设计一个数据库来存储图书信息,比如图书的名称、作者、出版社、价格、出版日期等数据。然后,在qt界面中设计相应的界面,实现对数据库进行增删改查操作。其中,需要使用qt中的数据库模块来实现与数据库的数据交互,比如使用QSqlDatabase类连接到数据库,使用QSqlQuery类执行查询语句等。 在实现具体功能时,需要考虑以下几个方面。 1.图书的添加:设计添加图书的界面,包括输入图书信息和上传图书图片等功能。在提交信息后,使用SQL语句将输入的图书信息插入到数据库中,并将上传的图书图片保存到本地。 2.图书的删除:设计删除图书的界面,在界面中显示当前数据库中的所有图书信息,并提供删除按钮。用户点击删除按钮后,使用SQL语句将该图书从数据库中删除。 3.图书的修改:设计修改图书的界面,显示当前图书的信息,允许用户修改图书信息,并提供确认修改按钮。用户点击确认修改按钮后,使用SQL语句将修改后的图书信息更新到数据库中。 4.图书的查询:设计查询图书的界面,提供根据关键词查询图书的功能。用户在输入关键词后,使用SQL语句查询符合条件的图书信息,并在界面中显示查询结果。 基于qt使用c++实现图书管理系统源码,需要综合运用qt的界面设计和数据库模块等技术,以实现对图书信息的管理和维护。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值