C++利用文件下载实现网络测速的总结

         最近项目中需要实现一个网络测速的功能,把尝试到的几种方式总结一下。 开发环境是Visual Studio 2010。

         一开始对网络测速这个功能有点误解,和当前网卡流量搞混了。毫不犹豫的找到了一个非常简单的方式,利用pdh.lib。这个库可以实现的功能可以参考Windows自带的工具perfmon.exe。在“Start”里搜索perfmon.exe可以看到。常见的CPU利用率,剩余内存,网络流量等都可以查到。

#include 
#include 
#include 
#pragma comment(lib,"pdh.lib")

int main()
{
    HQUERY query;
    PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query);
    if (status != ERROR_SUCCESS)
        std::cout << "Open Query Error" << std::endl;

    HCOUNTER counter;      
    counter = (HCOUNTER *)GlobalAlloc(GPTR, sizeof(HCOUNTER));
    WCHAR CounterPathBuffer[PDH_MAX_COUNTER_PATH]=  
        L"\\Network Interface(Intel[R] Ethernet Connection [2] I219-V)\\Bytes Received/Sec"; //Use perfMon.exe, add monitor can get the right counter name.
    status = PdhAddCounter(query, CounterPathBuffer, NULL, &counter);
    if (status != ERROR_SUCCESS)
        std::cout << "Add Counter Error" << std::endl;

    PdhCollectQueryData(query);      
    Sleep(1000);      
    PdhCollectQueryData(query);      

    PDH_FMT_COUNTERVALUE pdhValue;     
    DWORD dwValue;      
    status = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, &dwValue, &pdhValue);     

    if (status != ERROR_SUCCESS)         
        std::cout << "Get Value Error" << std::endl;

    std::cout << pdhValue.doubleValue << std::endl;

    PdhCloseQuery(query);      

    return 0; 
}
实现很简单,可惜是错的,看到的当前经过网卡的流量,不是网速。

        网速的测试只有利用文件下载,文件大小 / 下载时间 =  网络速度。偷懒又想走捷径,利用了Urlmon.lib里的URLDownloadToFile函数。

#include   
#include   
#include      
#pragma comment(lib,"URlmon")  

int main()  
{  
    char buffer[MAX_PATH];  
    _getcwd(buffer, MAX_PATH);  
    strcat_s(buffer, "//test.zip");
    //Clear cache first
   DeleteUrlCacheEntryA("http://www.nirsoft.net/utils/nircmd.zip"); 
    double end, cost;
    long Kbytes;
    double start = GetTickCount();
    HRESULT Result = URLDownloadToFileA(NULL, "http://www.nirsoft.net/utils/nircmd.zip", buffer, 0, NULL);  
    switch (Result)  
    {  
    case S_OK:
        end=GetTickCount(); 
        cost = (end - start)/1000;
        Kbytes = file_size/ cost /1024;
        std::cout << cost << "   speed is " <

简单,但是不对。URLDownloadToFileA封装了下载文件的全过程,网络连接,下载,写入文件。没有只提取下载部分的时间,导致网络测速的结果很不准确。如果希望每次都重新下载,记得要调用DeleteUrlCacheEntry函数。值得一提的是URLDownloadToFileA的最后一个参数,LPBINDSTATUSCALLBACK lpfnCB,根据MSDN的介绍:

lpfnCB

A pointer to the IBindStatusCallback interface of the caller. By using IBindStatusCallback::OnProgress, a caller can receive download status. URLDownloadToFile calls the IBindStatusCallback::OnProgress and IBindStatusCallback::OnDataAvailable methods as data is received. The download operation can be canceled by returning E_ABORT from any callback. This parameter can be set to NULL if status is not required.

看起来不错,也看到网上有人介绍利用这个参数在MFC中实现进度条的下载。但是我的体会是如果下载文件很小就完全没什么用,不准确。


之后尝试了CInternetSession, 需要#include

#define RECVPACK_SIZE 2048

bool DownloadSaveFiles(const char* url,const char* strSaveFile) {
    bool ret=false;
    CInternetSession Sess(_T("lpload"));
    Sess.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT     , 5000); //5 seconds time out
    Sess.SetOption(INTERNET_OPTION_SEND_TIMEOUT        , 5000); //5 seconds time out
    Sess.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT     , 5000); //5 seconds time out
    Sess.SetOption(INTERNET_OPTION_DATA_SEND_TIMEOUT   , 5000); //5 seconds time out
    Sess.SetOption(INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, 5000); //5 seconds time out
    DWORD dwFlag = INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD;

    CHttpFile* cFile   = NULL;
    char      *pBuf    = NULL;
    int        nBufLen = 0;
    do {  
        try{  
            cFile = (CHttpFile*)Sess.OpenURL((CString)url,1,dwFlag);
            DWORD dwStatusCode;
            cFile->QueryInfoStatusCode(dwStatusCode);
            if (dwStatusCode == HTTP_STATUS_OK) {
                DWORD nLen=0;
                cFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH, nLen);
                nBufLen=nLen;
                if (nLen <= 0) break;

                pBuf = (char*)malloc(nLen+8);
                ZeroMemory(pBuf,nLen+8);

                char *p=pBuf;
                double end, cost;
                double start = GetTickCount();
                while (nLen>0) {
                    int n = cFile->Read(p,(nLenClose();
        Sess.Close();
        delete cFile;
    }
    return ret;
}

这次看起来都对了,测试的时间也是比较准确的。问题出在引用的文件,afxinet.h,属于MFC的类。如果用在我们开放的软件中,需要打包整个MFC库,这肯定是不能接受的。

       由于afxinet其实使用的是wininet.lib,可以直接考虑利用这个库。这个库属于Microsoft SDKs,对于我们的软件来说可以接受。在网上找到了这篇文章,点击打开链接,里面的代码很完整,非常好,稍加修改用来做网络测速也很准确。谢谢原博主。

 

       还尝试了用socket来实现文件下载。代码太长就不贴了。整理了一个代码包,把列的几种方式都包含了进去。点击打开链接







你可能感兴趣的:(C++利用文件下载实现网络测速的总结)