欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
WinInet API 的異步方式使用
異步方式并不是什么高深莫測的事物,WinInet API 更是大家耳熟能詳。

如果你仔細看過(guò) MSDN 和 internet 上關(guān)于 WinInet API 的文章,你會(huì )發(fā)現盡管在很多篇章中提到了異步方式的使用,但是大部分說(shuō)明都只說(shuō)可以使用,而沒(méi)有說(shuō)如何使用。盡管如此,還是有一些文章可以給我們很多的提示,我會(huì )在后面列出。

由于網(wǎng)絡(luò )數據傳輸經(jīng)常會(huì )消耗一定的時(shí)間,因此我們總是把這些可能消耗時(shí)間的操作放到一個(gè)單獨的子線(xiàn)程,以免影響主線(xiàn)程正常的進(jìn)行??墒钱斪泳€(xiàn)程發(fā)生長(cháng)時(shí)間阻塞的時(shí)候,主線(xiàn)程由于某種原因需要退出,我們通常希望子線(xiàn)程能在主線(xiàn)程退出前正常退出。這時(shí)主線(xiàn)程就不得不 wait 子線(xiàn)程,這樣就導致主線(xiàn)程也被阻塞了。當然,主線(xiàn)程可以不 wait 子線(xiàn)程而自行退出,還可以使用 TerminateThread 強行終止子線(xiàn)程,但是這樣的后果通常是不可預料的,內存泄漏或許是最輕的一種危害了。

使用異步方式是解決這類(lèi)問(wèn)題的正確手段,下面我們根據一個(gè)實(shí)例來(lái)分析一下 WinInet API 異步方式的使用方法和注意事項。

我們的例子完成這樣的功能:給定一個(gè) URL (如:http://www.sina.com.cn/),使用 HTTP 協(xié)議下載該網(wǎng)頁(yè)或文件。我們一共創(chuàng )建了三個(gè)線(xiàn)程:主線(xiàn)程負責創(chuàng )建下載子線(xiàn)程,并等待子線(xiàn)程返回消息;子線(xiàn)程則使用異步方式的 WinInet API 完成下載任務(wù),并在各個(gè)階段返回消息給主線(xiàn)程;子線(xiàn)程還會(huì )創(chuàng )建一個(gè)回調函數線(xiàn)程,其作用我們稍后解釋。

實(shí)例代碼中涉及到一些線(xiàn)程,消息,事件,錯誤處理的 API,由于不是我討論的內容,就不仔細說(shuō)明了。

1. 主線(xiàn)程工作流程
a. 創(chuàng )建下載子線(xiàn)程
m_hMainThread = ::CreateThread(NULL,
  0,
  AsyncMainThread,
  this,
  NULL,
  &m_dwMainThreadID);

b. 等待子線(xiàn)程返回消息
MSG msg;
while (1)
{
  ::GetMessage(&msg, m_hWnd, 0, 0);

  if (msg.message == WM_ASYNCGETHTTPFILE)
  { //子線(xiàn)程發(fā)回消息
  switch(LOWORD(msg.wParam))
  {
  case AGHF_FAIL:
  {
  MessageBox(_T("下載行動(dòng)失敗結束!"));
  return;
  }
  case AGHF_SUCCESS:
  MessageBox(_T("下載行動(dòng)成功結束!"));
  return;
  case AGHF_PROCESS:
  //下載進(jìn)度通知
  break;
  case AGHF_LENGTH:
  //獲取下載文件尺寸通知
  break;
  }
  }

  DispatchMessage(&msg);
}

2. 下載子線(xiàn)程工作流程
a. 使用標記 INTERNET_FLAG_ASYNC 初始化 InternetOpen
m_hInternet = ::InternetOpen(m_szAgent,
  INTERNET_OPEN_TYPE_PRECONFIG,
  NULL,
  NULL,
  INTERNET_FLAG_ASYNC);
起步并不費勁,也不難理解,MSDN 上說(shuō)這樣設置之后,以后所有的 API 調用都是異步的了。
警惕......
看起來(lái)好像很簡(jiǎn)單,但是會(huì )有無(wú)數的陷阱等著(zhù)我們掉進(jìn)去。

b. 設置狀態(tài)回調函數 InternetSetStatusCallback
::InternetSetStatusCallback(m_hInternet, AsyncInternetCallback);
第一個(gè)陷阱就在這里等著(zhù)你呢,文獻[2]中提到使用一個(gè)單獨的線(xiàn)程來(lái)進(jìn)行這項設置,并解釋說(shuō)如果不這樣會(huì )有潛在的影響,而在其他文檔中卻沒(méi)有這樣使用的例子。盡管看起來(lái)多余,并且增加了一些復雜度,我們還是先把這種方法寫(xiě)出來(lái)再討論。子線(xiàn)程需要創(chuàng )建一個(gè)回調函數線(xiàn)程:
//重置回調函數設置成功事件
::ResetEvent(m_hEvent[0]);
m_hCallbackThread = ::CreateThread(NULL,
  0,
  AsyncCallbackThread,
  this,
  NULL,
  &m_dwCallbackThreadID);
//等待回調函數設置成功事件
::WaitForSingleObject(m_hEvent[0], INFINITE);
回調函數線(xiàn)程的實(shí)現如下:
DWORD WINAPI CAsyncGetHttpFile::AsyncCallbackThread(LPVOID lpParameter)
{
  CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile*)lpParameter;

  ::InternetSetStatusCallback(pObj->m_hInternet, AsyncInternetCallback);

  //通知子線(xiàn)程回調函數設置成功,子線(xiàn)程可以繼續工作
  ::SetEvent(pObj->m_hEvent[0]);
 
  //等待用戶(hù)終止事件或者子線(xiàn)程結束事件
  //子線(xiàn)程結束前需要設置子線(xiàn)程結束事件,并等待回調線(xiàn)程結束
  ::WaitForSingleObject(pObj->m_hEvent[2], INFINITE);
  return 0;
}
確實(shí)復雜了很多吧,雖然我試驗的結果發(fā)現兩種設置方法都能正確工作,但是確實(shí)發(fā)現了這兩種設置方法產(chǎn)生的一些不同效果,遺憾的是我沒(méi)有弄清具體的原因。我推薦大家使用后一種方法。

c. 打斷一下子線(xiàn)程的流程,由于回調函數和上一部分的關(guān)系如此密切,我們來(lái)看看它的實(shí)現
void CALLBACK CAsyncGetHttpFile::AsyncInternetCallback(
  HINTERNET hInternet,
  DWORD dwContext,
  DWORD dwInternetStatus,
  LPVOID lpvStatusInformation,
  DWORD dwStatusInformationLength)
{
  CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile*)dwContext;
  //在我們的應用中,我們只關(guān)心下面三個(gè)狀態(tài)
  switch(dwInternetStatus)
  {
  //句柄被創(chuàng )建
  case INTERNET_STATUS_HANDLE_CREATED:
  pObj->m_hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
  (lpvStatusInformation))->dwResult);
  break;
  //句柄被關(guān)閉
  case INTERNET_STATUS_HANDLE_CLOSING:
  ::SetEvent(pObj->m_hEvent[1]);
  break;
  //一個(gè)請求完成,比如一次句柄創(chuàng )建的請求,或者一次讀數據的請求
  case INTERNET_STATUS_REQUEST_COMPLETE:
  if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
  (lpvStatusInformation))->dwError)
  { //設置句柄被創(chuàng )建事件或者讀數據成功完成事件
  ::SetEvent(pObj->m_hEvent[0]);
  }
  else
  { //如果發(fā)生錯誤,則設置子線(xiàn)程退出事件
  //這里也是一個(gè)陷阱,經(jīng)常會(huì )忽視處理這個(gè)錯誤,
  ::SetEvent(pObj->m_hEvent[2]);
  }
  break;
  }
}

d. 繼續子線(xiàn)程的流程,使用 InternetOpenUrl 完成連接并獲取下載文件頭信息
//重置句柄被創(chuàng )建事件
::ResetEvent(m_hEvent[0]);
m_hFile = ::InternetOpenUrl(m_hInternet,
  m_szUrl,
  NULL,
  NULL,
  INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD,
  (DWORD)this);
if (NULL == m_hFile)
{
  if (ERROR_IO_PENDING == ::GetLastError())
  {
  if (WaitExitEvent())
  {
  return FALSE;
  }
  }
  else
  {
  return FALSE;
  }
}
等我們把 WaitExitEvent 函數的實(shí)現列出在來(lái)再解釋發(fā)生的一切:
BOOL CAsyncGetHttpFile::WaitExitEvent()
{
  DWORD dwRet = ::WaitForMultipleObjects(3, m_hEvent, FALSE, INFINITE);
  switch (dwRet)
  {
  //句柄被創(chuàng )建事件或者讀數據請求成功完成事件
  case WAIT_OBJECT_0:
  //句柄被關(guān)閉事件
  case WAIT_OBJECT_0+1:
  //用戶(hù)要求終止子線(xiàn)程事件或者發(fā)生錯誤事件
  case WAIT_OBJECT_0+2:
  break;
  }
  return WAIT_OBJECT_0 != dwRet;
}
在這里我們終于看到異步方式的巨大優(yōu)勢了,InternetOpenUrl 函數要完成域名解析,服務(wù)器連接,發(fā)送請求,接收返回頭信息等任務(wù),異步方式中 InternetOpenUrl 并不等待成功創(chuàng )建了 m_hFile 才返回,我們看到 m_hFile 是可以在回調函數中賦值的。如果 InternetOpenUrl 的返回值為 NULL 并且 GetLastError 返回 ERROR_IO_PENDING,我們使用 WaitForMultipleObjects 來(lái)等待請求的成功完成,這樣主線(xiàn)程就有機會(huì )在這個(gè)等待過(guò)程中終止子線(xiàn)程的操作。我真是迫不及待的想把主線(xiàn)程如何強行終止子線(xiàn)程的代碼列出來(lái)了:
//設置要求子線(xiàn)程結束事件
::SetEvent(m_hEvent[2]);
//等待子線(xiàn)程安全退出
::WaitForSingleObject(m_hMainThread, INFINITE);
//關(guān)閉線(xiàn)程句柄
::CloseHandle(m_hMainThread);
哈哈,不需要使用 TerminateThread 終止線(xiàn)程,一切都是安全的,可預料的。
我們再考慮一種情況,這種情況好得超乎你的想象,InternetOpenUrl 返回了一個(gè)非空的 m_hFile 怎么辦?呵呵,這說(shuō)明 InternetOpenUrl 已經(jīng)成功創(chuàng )建了一個(gè) m_hFile,并且沒(méi)有發(fā)生任何阻塞,都不用等待任何事件,直接繼續下一步吧。
最后需要說(shuō)明得是,InternetOpenUrl 的最后一個(gè)參數會(huì )被作為回調函數的第二個(gè)參數使用。并且哪怕在回調函數中不需要這個(gè)參數,這個(gè)值你也不能設置為 0,否則 InternetOpenUrl 將不會(huì )按照異步的方式工作。
到這里,我們已經(jīng)將 WinInet API 的異步方式使用的關(guān)鍵部分都展示了,你應該可以使用 WinInet API 的異步方式寫(xiě)出你自己的應用了。不過(guò)還是讓我們繼續完成這個(gè)實(shí)例的其他部分。

e. 使用 HttpQueryInfo 分析頭信息
DWORD dwStatusSize = sizeof(m_dwStatusCode);
if (FALSE == ::HttpQueryInfo(m_hFile,
  HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  &m_dwStatusCode,
  &dwStatusSize,
  NULL)) //獲取返回狀態(tài)碼
{
  return FALSE;
}
//判斷狀態(tài)碼是不是 200
if (HTTP_STATUS_OK != m_dwStatusCode)
{
  return FALSE;
}
DWORD dwLengthSize = sizeof(m_dwContentLength);
if (FALSE == ::HttpQueryInfo(m_hFile,
  HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  &m_dwContentLength,
  &dwLengthSize,
  NULL)) //獲取返回的Content-Length
{
  return FALSE;
}
...//通知主線(xiàn)程獲取文件大小成功
需要說(shuō)明的是 HttpQueryInfo 并不進(jìn)行網(wǎng)絡(luò )操作,因此它不需要進(jìn)行異步操作的處理。

f. 使用標記 IRF_ASYNC 讀數據 InternetReadFileEx
//為了向主線(xiàn)程報告進(jìn)度,我們設置每次讀數據最多 1024 字節
for (DWORD i=0; i<m_dwContentLength; )
{
  INTERNET_BUFFERS i_buf = {0};
  i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
  i_buf.lpvBuffer = new TCHAR[1024];
  i_buf.dwBufferLength = 1024;

  //重置讀數據事件
  ::ResetEvent(m_hEvent[0]);
  if (FALSE == ::InternetReadFileEx(m_hFile,
  &i_buf,
  IRF_ASYNC,
  (DWORD)this))
  {
  if (ERROR_IO_PENDING == ::GetLastError())
  {
  if (WaitExitEvent())
  {
    delete[] i_buf.lpvBuffer;
    return FALSE;
  }
  }
  else
  {
  delete[] i_buf.lpvBuffer;
  return FALSE;
  }
  }
  else
  {
  //在網(wǎng)絡(luò )傳輸速度快,步長(cháng)較小的情況下,
  //InternetReadFileEx 經(jīng)常會(huì )直接返回成功,
  //因此要判斷是否發(fā)生了用戶(hù)要求終止子線(xiàn)程事件。
  if (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEvent[2], 0))
  {
  ::ResetEvent(m_hEvent[2]);
  delete[] i_buf.lpvBuffer;
  return FALSE;
  }
  }
  i += i_buf.dwBufferLength;
  ...//保存數據
  ...//通知主線(xiàn)程下載進(jìn)度
  delete[] i_buf.lpvBuffer;
}
這里 InternetReadFileEx 的異步處理方式同 InternetOpenUrl 的處理方式類(lèi)似,我沒(méi)有使用 InternetReadFile 因為它沒(méi)有異步的工作方式。

g. 最后清理戰場(chǎng),一切都該結束了
//關(guān)閉 m_hFile
::InternetCloseHandle(m_hFile);
//等待句柄被關(guān)閉事件或者要求子線(xiàn)程退出事件
while (!WaitExitEvent())
{
  ::ResetEvent(m_hEvent[0]);
}
//設置子線(xiàn)程退出事件,通知回調線(xiàn)程退出
::SetEvent(m_hEvent[2]);
//等待回調線(xiàn)程安全退出
::WaitForSingleObject(m_hCallbackThread, INFINITE);
::CloseHandle(m_hCallbackThread);
//注銷(xiāo)回調函數
::InternetSetStatusCallback(m_hInternet, NULL);
::InternetCloseHandle(m_hInternet);
...//通知主線(xiàn)程子線(xiàn)程成功或者失敗退出


實(shí)例中,我們建立一個(gè)完整的 HTTP 下載程序,并且可以在主線(xiàn)程中對下載過(guò)程進(jìn)行完全的監控。我們使用了 WinInet API 中的這些函數:
InternetOpen
InternetSetStatusCallback
InternetOpenUrl
HttpQueryInfo
InternetReadFileEx
InternetCloseHandle
其中 InternetOpenUrl 和 InternetReadFileEx 函數是按照異步方式工作的,文獻[4]中列出了可以按照異步方式工作的 API:
FtpCreateDirectory
FtpDeleteFile
FtpFindFirstFile
FtpGetCurrentDirectory
FtpGetFile
FtpOpenFile
FtpPutFile
FtpRemoveDirectory
FtpRenameFile
FtpSetCurrentDirectory
GopherFindFirstFile
GopherOpenFile
HttpEndRequest
HttpOpenRequest
HttpSendRequestEx
InternetConnect
InternetOpenUrl
InternetReadFileEx


參考文獻:
1. http://www.codeproject.com/internet/asyncwininet.asp
2. MSDN: <Technical Articles\Web Development\Authoring and Programming\Advanced FTP, or Teaching Fido To Phetch>
3. MSDN: <Platform SDK Documentation\Web Development\Internet Development SDK\Win32 Internet Functions\Common Functions>
4. MSDN: <Platform SDK Documentation\Web Development\Internet Development SDK\Win32 Internet Functions\Tutorials\Calling Win32 Internet Functions Asynchronously>
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Windows系統編程之異步I/O和完成端口
《Windows核心編程系列》十談?wù)????同步設備IO與異步設備IO之異步IO
使用InternetOpenUrl掛起的一個(gè)解決方案
用Win32 API實(shí)現串行通信
公用函數
wininet相關(guān)函數
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久