察看本文應(yīng)用于的產(chǎn)品
文章編號(hào) : 138692
最后修改 : 2005年8月24日
修訂 : 1.1
本文的發(fā)布號(hào)曾為 CHS138692
本頁
概要
更多信息
BOOL SetTimeOut(UINT uTimeOut)
BOOL KillTimeOut()
BOOL OnMessagePending()
示例代碼
參考
概要
CSocket 操作,如“接收”(Receive)、“發(fā)送”(Send) 和“連接”(Connect) 均是阻塞操作,即要等到操作成功執(zhí)行完畢或套接字上出現(xiàn)錯(cuò)誤后,對(duì)這些函數(shù)的調(diào)用才有返回結(jié)果。
在某些情況下,操作可能永遠(yuǎn)不能成功完成,這將導(dǎo)致程序無限循環(huán)等待操作完成。一種解決方法是通過編程限制完成操作使用的時(shí)間。本文將討論這種方法。
?回到頂端
更多信息
這種方法是設(shè)置定時(shí),讓它在操作時(shí)間過長(zhǎng)時(shí)啟動(dòng)。此方法的關(guān)鍵在于處理定時(shí)器的方式。雖然操作是“阻塞的”,但仍然可以處理到達(dá)的消息。如果通過使用 SetTimer 設(shè)置定時(shí)器,那么可以查找 WM_TIMER 消息,并在收到該消息時(shí)終止操作。該過程中涉及的主要函數(shù)有:
Windows API 調(diào)用函數(shù):
::SetTimer
MFC 函數(shù):
CSocket::OnMessagePending
CSocket::CancelBlockingCall
為簡(jiǎn)單起見,可以在 Csocket 衍生類中封裝該功能。
警告:在進(jìn)一步閱讀本文之前,請(qǐng)注意在某些 MFC 版本中存在錯(cuò)誤,會(huì)在試圖使用定時(shí)器并重疊 OnMessagePending 時(shí)引起問題。這一問題將在下面的 Microsoft Knowledge Base 文章中進(jìn)行討論:
137632 (http://support.microsoft.com/kb/137632/EN-US/) 錯(cuò)誤:定時(shí)器激活時(shí)未調(diào)用 OnMessagePending
本文僅適用于 Visual C++ 的 1.52、1.52b、2.1 和 2.2 版本。如果使用的是這些 Visual C++ 版本之一,則還需要實(shí)施所提供的變通解決方法。
本文最后部分顯示提供這種超時(shí)功能的類的示例代碼。以下內(nèi)容講述由該類實(shí)現(xiàn)的函數(shù)。
?回到頂端
BOOL SetTimeOut(UINT uTimeOut)
調(diào)用此函數(shù)之后僅接著調(diào)用 CSocket 函數(shù)(如 Receive、Send 和 Accept)。uTimeOut 參數(shù)是以毫秒為單位指定的。之后,進(jìn)行定時(shí)器的設(shè)置。如果設(shè)置定時(shí)器失敗,那么函數(shù)返回 FALSE。有關(guān)詳細(xì)情況,請(qǐng)參閱 SetTimer 函數(shù)的 Windows API 文檔。
?回到頂端
BOOL KillTimeOut()
在完成阻塞操作后,必須調(diào)用此函數(shù)。此函數(shù)刪除用 SetTimeOut 設(shè)置的定時(shí)器。如果調(diào)用 KillTimer 失敗,則返回 FALSE。有關(guān)詳細(xì)情況,請(qǐng)參閱 KillTimer 函數(shù)的 Windows API 文檔。
?回到頂端
BOOL OnMessagePending()
這是一個(gè)虛擬回調(diào)函數(shù),在等待操作完成時(shí)由 CSocket 類進(jìn)行調(diào)用。此函數(shù)給您提供處理傳入消息的機(jī)會(huì)。此實(shí)施過程檢查用 SetTimeOut 調(diào)用函數(shù)設(shè)置的定時(shí)器的 WM_TIMER 消息。如果收到消息,則調(diào)用 CancelBlockingCall 函數(shù)。有關(guān) OnMessagePending 和 CancelBlockingCall 函數(shù)詳細(xì)的信息,請(qǐng)參閱 MFC 文檔。請(qǐng)注意:調(diào)用 CancelBlockingCall 函數(shù) 將導(dǎo)致操作失敗,而且 GetLastError 函數(shù)返回 WSAEINTR(表示操作中斷)。
下面是使用該類的一個(gè)例子:
?? ...
?? CTimeOutSocket sockServer;
?? CAcceptedSocket sockAccept;
?? sockServer.Create(777);
?? sockServer.Listen();
?? // Note the following sequence:
?? //? SetTimeOut
?? //?
?? //? KillTimeOut
?? if(!sockServer.SetTimeOut(10000))
?? {
???? ASSERT(FALSE);
???? // Error Handling...for some reason, we could not setup
???? // the timer.
?? }
?? if(!sockServer.Accept(sockAccept))
?? {
???? int nError = GetLastError();
???? if(nError==WSAEINTR)
?????? AfxMessageBox("No Connections Arrived For 10 Seconds");
????? else
??????? ; // Do other error processing.
?? }
?? if(!sockServer.KillTimeOut())
?? {
???? ASSERT(FALSE);
???? // Error Handling...for some reason the timer could not
???? // be destroyed...perhaps a memory overwrite has changed
???? // m_nTimerID?
???? //
?? }
?? ...
?回到頂端
示例代碼
?
?? //
?? // HEADER FILE
?? //
?? class CTimeOutSocket : public CSocket
?? {
?? public:
???? BOOL SetTimeOut(UINT uTimeOut);
???? BOOL KillTimeOut();
?? protected:
???? virtual BOOL OnMessagePending();
?? private:
???? int m_nTimerID;
?? };
?? //
?? // END OF FILE
?? //
?? //
?? // IMPLEMENTATION FILE
?? //
?? BOOL CTimeOutSocket::OnMessagePending()
?? {
???? MSG msg;
???? if(::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE))
???? {
?????? if (msg.wParam == (UINT) m_nTimerID)
?????? {
???????? // Remove the message and call CancelBlockingCall.
???????? ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
???????? CancelBlockingCall();
???????? return FALSE;? // No need for idle time processing.
?????? };
???? };
???? return CSocket::OnMessagePending();
?? }
?? BOOL CTimeOutSocket::SetTimeOut(UINT uTimeOut)
?? {
???? m_nTimerID = SetTimer(NULL,0,uTimeOut,NULL);
???? return m_nTimerID;
?? }
?? BOOL CTimeOutSocket::KillTimeOut()
?? {
???? return KillTimer(NULL,m_nTimerID);
?? }
CSOCKET的超時(shí)設(shè)置和UDP發(fā)送接收
??? 使用CSoket多次了,但對(duì)于它的block模式的理解并不是很深入。昨天使用csoket的udp多發(fā)測(cè)試(server接到數(shù)據(jù)后,需要通過某種方式將數(shù)據(jù)發(fā)送到client,使用tcp方式比較可靠,我一直這樣用的,但是比較費(fèi)時(shí),需要逐一發(fā)送),發(fā)現(xiàn)了問題:
? 1)create(),sendto(),receivefrom()....
? 2)其中,發(fā)送方一直定時(shí)發(fā)送數(shù)據(jù)無問題;
? 3)而接收方,通過一個(gè)單獨(dú)的接收線程實(shí)現(xiàn)( 注意:csocket不能跨線程使用!主線程中socket create()后,detach()并將sock作為lpvoid傳入接收線程 )。代碼如下:
? //處理接收數(shù)據(jù)
UINT CSockSvr::DealSvrRevData(LPVOID lParam)
{
? ......
?DWORD dwError;
?TCHAR cBuff[1000];
?CString sIP;
?UINT uPort;
?for(;!pDlg->m_bExit;)
?{
? ::memset( cBuff,0,sizeof(cBuff) );
? // 如果沒有接到數(shù)據(jù),一直等待。。。。
? // 阻塞模式的弊端:::在退出時(shí)候,通過CancelBlockingCall
?? int iRst=SockSvr.ReceiveFrom( cBuff,sizeof(cBuff),sIP,uPort,0 );
??? if( iRst!=SOCKET_ERROR )
? {
?? CString sTemp=cBuff;
?? TRACE1( _T("\r\n Rev Data: %s\r\n"),sTemp );
? }
? else
? {
?? dwError=GetLastError();
?? TRACE1( _T("\r\n Rev Data Error code: %d\r\n"),dwError );
? }
? ::Sleep(200);
?}
?
?return 0;
}
? 問題:如果發(fā)送方?jīng)]有數(shù)據(jù),SockSvr.ReceiveFrom()會(huì)一致處于等待狀態(tài),導(dǎo)致整個(gè)處理接收線程的停止,如果用戶需要退出/結(jié)束程序,無法正確釋放資源(無法關(guān)閉在線程函數(shù)重打開的socket--不能跨線程操作socket,無法關(guān)閉線程),造成系統(tǒng)memory leak...
? 針對(duì)這種問題,微軟的解決辦法是:http://support.microsoft.com/default.aspx?scid=kb;zh-cn;138692 ,經(jīng)過測(cè)試,在socket進(jìn)行connect()的時(shí)候不好用。后來,結(jié)合微軟的辦法,我通過自定義的定時(shí)器,實(shí)現(xiàn)了block超時(shí)間后自動(dòng)退出的功能〉
? #if !defined(AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_)
#define AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TimeOutSock.h : header file
//
/*
CSocket 操作,如接收(Receive)、發(fā)送(Send) 和連接(Connect) 均是阻塞操作,
?即要等到操作成功執(zhí)行完畢或套接字上出現(xiàn)錯(cuò)誤后,對(duì)這些函數(shù)的調(diào)用才有返回結(jié)果。
某些情況下,操作可能永遠(yuǎn)不能成功完成,這將導(dǎo)致程序無限循環(huán)等待操作完成。
?一種解決方法是通過編程限制完成操作使用的時(shí)間。本文將討論這種方法。
*/
/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock command target
class CTimeOutSock : public CSocket
{
// Attributes
public:
// Operations
public:
?CTimeOutSock();
?virtual ~CTimeOutSock();
// Overrides
public:
?// ClassWizard generated virtual function overrides
?//{{AFX_VIRTUAL(CTimeOutSock)
?public:
?virtual BOOL OnMessagePending();
?//}}AFX_VIRTUAL
?// Generated message map functions
?//{{AFX_MSG(CTimeOutSock)
? // NOTE - the ClassWizard will add and remove member functions here.
?//}}AFX_MSG
/*
//定時(shí)器設(shè)置block超時(shí) 不是很好用。。。徐衛(wèi)話 2006.4.19
CSocket 操作,如接收(Receive)、發(fā)送(Send) 和連接(Connect) 均是阻塞操作,
?即要等到操作成功執(zhí)行完畢或套接字上出現(xiàn)錯(cuò)誤后,對(duì)這些函數(shù)的調(diào)用才有返回結(jié)果。
某些情況下,操作可能永遠(yuǎn)不能成功完成,這將導(dǎo)致程序無限循環(huán)等待操作完成。
?一種解決方法是通過編程限制完成操作使用的時(shí)間。本文將討論這種方法。
*/
// 自己計(jì)算時(shí)間的辦法 OK
public:
???? BOOL SetTimeOut(UINT uTimeOut=1000);
???? BOOL KillTimeOut();
private:
? LONGLONG m_llDtStart;
? UINT? m_uTimeOut;
};
/////////////////////////////////////////////////////////////////////////////
//}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_)
? // TimeOutSock.cpp : implementation file
//
#include "stdafx.h"
#include "NetBroad.h"
#include "TimeOutSock.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock
CTimeOutSock::CTimeOutSock()
{
}
CTimeOutSock::~CTimeOutSock()
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CTimeOutSock, CSocket)
?//}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock member functions
//設(shè)置超時(shí)
BOOL CTimeOutSock::SetTimeOut(UINT uTimeOut)
{??
?//get start cnt
?LARGE_INTEGER llCnt;
?::QueryPerformanceCounter(&llCnt);
?m_llDtStart=llCnt.QuadPart;
?m_uTimeOut=uTimeOut;
???? return TRUE;
}
//刪除超時(shí)參數(shù)
BOOL CTimeOutSock::KillTimeOut()
{
?m_llDtStart=0;//表明取消計(jì)時(shí)
?return TRUE;
}
//檢查是否超時(shí)間
BOOL CTimeOutSock::OnMessagePending()
{
?// TODO: Add your specialized code here and/or call the base class
?/*
?MSG msg;
? if(::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE))
? {
?? if (msg.wParam == (UINT) m_nTimerID)
?? {
??? // Remove the message and call CancelBlockingCall.
??? ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
??? CancelBlockingCall();
??? return FALSE;? // No need for idle time processing.
?? };
? };
?*/
?if( m_llDtStart )
?{
? LARGE_INTEGER lldtEnd;
? ::QueryPerformanceCounter(&lldtEnd);?
? LARGE_INTEGER llFrq;
? ::QueryPerformanceFrequency(&llFrq);
? double dbDealy=(double)(lldtEnd.QuadPart-m_llDtStart)*1000/llFrq.QuadPart;
? if( dbDealy>m_uTimeOut )
? {
?? CancelBlockingCall();
?? return FALSE;? // No need for idle time processing.
? }
?}
?
?return CSocket::OnMessagePending();
}
?
? 經(jīng)過改進(jìn)后, 對(duì)處理接收線成的函數(shù)進(jìn)行了部分改進(jìn):
? SockSvr.SetTimeOut(500);? int iRst=SockSvr.ReceiveFrom( cBuff,sizeofcBuff),sIP,uPort,0 );? SockSvr.KillTimeOut();
?當(dāng)block超過定時(shí)后,socket自動(dòng)退出block,防止接收線程停止。問題終于解決了!
from: http://www.arm8.com/cv/1/6/197.html