Wininet异步读取类CHttpClient

来源:互联网 发布:免费网页游戏源码 编辑:程序博客网 时间:2024/06/16 13:55
      项目在使用Wininet API时一直采用的同步模式,通过开一个线程+等待超时的“假"异步方式来实现非阻塞调用。但是最近突然发现,同步调用InternetOpenUrl在处于阻塞状态时,在某些情况下,通过InternetCloseHandle无法强制InternetOpenUrl立即返回。导致程序退出时开启的线程无法正常退出。排查了很久,未找到问题原因,怀疑这是同步方式本身的bug。所以不得已引入了异步模式。现封装了一个异步读取类CHttpClient,采用全异步方式读取,可以自定义接口超时时间,通过使用异步模式后,很好的避免了上述问题的发生。

class CHttpClient{public:CHttpClient(void);~CHttpClient(void);public:BOOL Open(LPCTSTR lpUrl,int timeout = INFINITE);int Read(unsigned char* buffer,int size,int timeout = INFINITE);void Close();};


客户端调用示例代码:

int main(int argc, char *argv[]){CHttpClient httpClient;BOOL bRet = httpClient.Open("http://192.168.169.200/stream.php?cam=c0a86b8500",5000);if(!bRet){return 0;}unsigned char buffer[4096] = {0};while(1){int len = httpClient.Read(buffer,sizeof(buffer));if(len <= 0){break;}}httpClient.Close();return 1;}
CHttpClient完整的代码如下:


/********************************************************************  filename:   HttpClient.hcreated:    2016-04-08author:     firehood  purpose:    A Asynchronous Http Client By Using WinInet HTTP functions*********************************************************************/#pragma once#include <windows.h>#include <wininet.h>class CHttpClient{public:CHttpClient(void);~CHttpClient(void);public:BOOL Open(LPCTSTR lpUrl,int timeout = INFINITE);int Read(unsigned char* buffer,int size,int timeout = INFINITE);void Close();private:static void CALLBACK HttpStatusCallback(HINTERNET hInternet,DWORD dwContext,DWORD dwInternetStatus,LPVOID lpStatusInfo,DWORD dwStatusInfoLen);private:HINTERNET  m_hInternet;HINTERNET  m_hSession;HANDLE  m_hRequestOpenedEvent;HANDLE  m_hRequestCompleteEvent;DWORD   m_dwCompleteResult;};

CHttpClient.cpp

/********************************************************************  filename:   HttpClient.cppcreated:    2016-04-08author:     firehood  purpose:    A Asynchronous Http Client By Using WinInet HTTP functions*********************************************************************/#include "HttpClient.h"#include <tchar.h>#pragma comment(lib,"wininet.lib")CHttpClient::CHttpClient(void): m_hInternet(NULL), m_hSession(NULL), m_dwCompleteResult(0){m_hRequestOpenedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);m_hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);}CHttpClient::~CHttpClient(void){Close();if(m_hRequestOpenedEvent){CloseHandle(m_hRequestOpenedEvent);m_hRequestOpenedEvent = NULL;}if(m_hRequestCompleteEvent){CloseHandle(m_hRequestCompleteEvent);m_hRequestCompleteEvent = NULL;}}BOOL CHttpClient::Open(LPCTSTR lpUrl,int timeout){if(lpUrl == NULL){return FALSE;}if(m_hInternet){Close();}BOOL bRet = FALSE;do{m_hInternet = InternetOpen(NULL,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC);if (m_hInternet == NULL){break;}// Setup callback functionif (InternetSetStatusCallback(m_hInternet,HttpStatusCallback) == INTERNET_INVALID_STATUS_CALLBACK){break;}m_hSession = InternetOpenUrl(m_hInternet,lpUrl,NULL,NULL,INTERNET_FLAG_NO_CACHE_WRITE,(DWORD_PTR)this);if(NULL == m_hSession){if (GetLastError() != ERROR_IO_PENDING){break;}// Wait until we get the connection handleif(WaitForSingleObject(m_hRequestOpenedEvent,timeout) == WAIT_TIMEOUT){break;}}if(WaitForSingleObject(m_hRequestCompleteEvent,timeout) == WAIT_TIMEOUT){break;}if(m_dwCompleteResult == 0){break;}DWORD dwStatusCode;TCHAR responseText[256] = {0};DWORD responseTextSize = sizeof(responseText);if(!HttpQueryInfo(m_hSession,HTTP_QUERY_STATUS_CODE,&responseText,&responseTextSize,NULL)){break;}dwStatusCode = _ttoi(responseText);if(dwStatusCode != HTTP_STATUS_OK ){break;}bRet = TRUE;}while(0);if(!bRet){if(m_hSession){InternetCloseHandle(m_hSession);m_hSession = NULL;}if(m_hInternet){InternetSetStatusCallback(m_hInternet, NULL);InternetCloseHandle(m_hInternet);m_hInternet = NULL;}}return bRet;}int CHttpClient::Read(unsigned char* buffer,int size,int timeout){if(buffer == NULL || size <= 0){return -1;}INTERNET_BUFFERS InetBuff;memset(&InetBuff, 0, sizeof(InetBuff));InetBuff.dwStructSize = sizeof(InetBuff);InetBuff.lpvBuffer = buffer;InetBuff.dwBufferLength = size;if(!InternetReadFileEx(m_hSession,&InetBuff,0,(DWORD_PTR)this)){if (GetLastError() == ERROR_IO_PENDING){if(WaitForSingleObject(m_hRequestCompleteEvent,timeout) == WAIT_TIMEOUT){return -1;}}else{return -1;}}return InetBuff.dwBufferLength;}void CHttpClient::Close(){SetEvent(m_hRequestOpenedEvent);SetEvent(m_hRequestCompleteEvent);if(m_hSession){InternetCloseHandle(m_hSession);m_hSession = NULL;}if(m_hInternet){InternetSetStatusCallback(m_hInternet, NULL);InternetCloseHandle(m_hInternet);m_hInternet = NULL;}ResetEvent(m_hRequestOpenedEvent);ResetEvent(m_hRequestCompleteEvent);}void CALLBACK CHttpClient::HttpStatusCallback(HINTERNET hInternet,DWORD dwContext,DWORD dwInternetStatus,LPVOID lpStatusInfo,DWORD dwStatusInfoLen){CHttpClient *p = (CHttpClient*)dwContext;switch(dwInternetStatus){case INTERNET_STATUS_HANDLE_CREATED:{INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;p->m_hSession = (HINTERNET)pRes->dwResult;SetEvent(p->m_hRequestOpenedEvent);}break;case INTERNET_STATUS_REQUEST_COMPLETE:{INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;p->m_dwCompleteResult = pRes->dwResult;SetEvent(p->m_hRequestCompleteEvent);}break;case INTERNET_STATUS_HANDLE_CLOSING:break;case INTERNET_STATUS_RESPONSE_RECEIVED:break;default:break;}}

参考文章:http://www.codeproject.com/Articles/822/Using-WinInet-HTTP-functions-in-Full-Asynchronous

1 0