在某個同事留下的網路作業碼中,如果使用者試圖從網路開啟一個實際並不存在的URL, 走到InternetOpenUrl這步,應用程式就一命嗚呼了。這個API並沒有按照文檔上說的那樣返回一個NULL給檔案HANDLE,而是完全停止回應。他嘗試使用InternetSetOption來設定逾時的時限,代碼裡這樣寫
DWORD n = 10000;
InternetSetOption(p->Internet, INTERNET_OPTION_CONNECT_TIMEOUT, &n, sizeof(n));
但沒有實際效果。從後文可以猜測,InternetOpenUrl再去調用了網路的IO處理,在IO處理中被卡死,罪過似乎並不在InternetOpenUrl和wininet.dll這層上.
於是他在前面某次工作記錄裡說已經解決了這個問題,然後又被測試組罵回來說沒效果. 我搜到了開原始碼裡一個很好的演算法, 自己測試了下,的確有效。讓我們來看下它的演算法,確實很精妙. 整個演算法的核心是用非同步方式來處理wininet.dll提供的那些函數
具體實現代碼如下:
(1) 首先, 在InternetOpen時修改了參數
它用的是 InternetOpen(T("MyApp"), p->AccessType, NULL,NULL, INTERNET_FLAG_ASYNC);
而會卡死的情況下用的是 InternetOpen(T("AmoiPlayer"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
前面幾個參數不是影響該效能的關鍵,關鍵點在最後一個參數上: INTERNET_FLAG_ASYNC. 在文檔裡對這個參數的解釋是 Makes only asynchronous requests on handles descended from the handle returned from this function. 寫得有些晦澀. 實際效果就是如果在InternetOpen中設定為INTERNET_FLAG_ASYNC, 那麼在InternetOpenUrl時就會立刻返回, 如果為NULL就必須有具體的傳回值才肯RETRUN.
(2) 註冊一個CALLBACK函數 InternetSetStatusCallback(p->Internet, InternetCallback)
因為我們OpenURL操作會立刻返回了,那麼什麼時候能得到有效或無效的HANDLE呢? 這就需要一個CALLBACK來做這件事情. 這裡當然有必要看下這個CALLBACK函數裡做了什麼
static void CALLBACK InternetCallback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
INTERNET_ASYNC_RESULT *Result = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
http *p = (http*)dwContext;
p->CompleteResult = Result->dwResult;
SetEvent(p->Complete);
}
}
當等到INTERNET_STATUS_REQUEST_COMPLETE通知的時候, 把RESULT擷取下來放在p->CompleteResult裡面,然後SetEvent. 也就是說系統CALLBACK線程和其他什麼線程進行通訊了
(3) 在InternetOpenUrl的時候代碼如下:
Result = (DWORD)InternetOpenUrl(p->Internet, URL, s, (DWORD)-1, INTERNET_FLAG_NO_CACHE_WRITE|Secure, (DWORD)p);
p->Handle = (HANDLE)Pending(p, Result , 30);
if (Result || GetLastError() != ERROR_IO_PENDING)
{
p->Handle = Result;
}
else
{
p->Pending = 1;
if ( WaitForSingleObject(p->Complete,300000) == WAIT_OBJECT_0 )
{
p->Pending =0;
p->Handle = p->CompleteResult;
}
else
{
p->Handle = NULL;
}
}
代碼中把流程圖中的左邊線程, 從調用API觸發CALLBACK開始, 到底端取得HANDEL這塊演算法封裝為一個函數, 可以被其他對wininet.dll介面函數的操作共用, 如InternetReadFile等.
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/li_guotao/archive/2009/02/20/3915102.aspx