为CSocket配置Time-Out功能

本文介绍如何为CSocket配置超时功能,通过设置计时器并利用SetTimer、KillTimer等函数,在操作如Send()、Receive()长时间未完成时中断操作。
为CSocket配置Time-Out功能
    CSocket操作,如Send(),Receive(),Connect()都属阻塞操作,即它们在成功完成或错误发生之前是不会返回的。
    在某些情况下,某项操作可能永远不能成功完成,程序为了等待其完成就得永远循环下去。在程序中为某项操作限定一个成功完成的时间是个好主意。本文就是讨论此问题的。
    一个办法是设计一个计时器,当操作费时过长时就触发。这个办法的关键是怎样处理计时器。虽然操作是"阻塞"的,但仍具处理传回的消息的能力。如果用SetTimer来设置计时器,就可截获WM_TIMER消息,当它产生时就终止操作。涉及到这个过程的主要函数是:Windows API ::SetTimer(),MFC函数CSocket::OnMessagePending()和CSocket:: CancelBlockingCall()。这些功能可包装到你的CSocket类中得以简化。
    类中用到三个重要函数:
    BOOL SetTimeOut(UINT uTimeOut) 它应在CSocket函数调用前被调用。uTimeOut以千分秒为单位。下面的实现只是简单的设置计时器。当设置计时器失败时返回False。参见Windows API中关于SetTimer的说明。
    BOOL KillTimeOut() 此函数应在操作未完成被阻塞时被调用。它删除SetTimeOut所设置的计时器。如果调用KillTimer失败则返回False。参见Windows API中关于KillTimer的说明。
    BOOL OnMessagePending() 它是一个虚拟回调函数,当等待操作完成时被CSocket类调用。它给你机会来处理传回的消息。这次我们用它来检查SetTimeOut所设置的计时器,如果超时(Time-Out),则它调用CancelBlockingCall()。参见MFC文档关于OnMessagePending()和CancelBlockingCall()的说明。注意调用CancelBlockingCall()将使当前操作失败,GetLastError()函数返回WSAEINTR(指出是中断操作)。
    下面就是使用这个类的例子:
   ...   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);   }



UINT CDialogNetwork::RecvThread(LPVOID pParam) { CDialogNetwork* pThis = (CDialogNetwork*)pParam; char buf[4096]; // 设置套接字为非阻塞模式 u_long iMode = 1; ioctlsocket(pThis->mSocket[0], FIONBIO, &iMode); while (pThis->mBConnected[0] || pThis->mBConnected[1]) { fd_set readSet; FD_ZERO(&readSet); // 添加两个套接字到监控集合 if (pThis->mBConnected[0]) FD_SET(pThis->mSocket[0], &readSet); // 设置500ms超时 $t = 500ms$ timeval timeout = { 0, 500000}; // 使用select检测套接字可读状态 int res = select(0, &readSet, NULL, NULL, &timeout); if (res == SOCKET_ERROR) break; // 错误处理 if (res > 0 && FD_ISSET(pThis->mSocket[0], &readSet)) { int len = 0; if (pThis->mRadioTCP[0].GetCheck() == BST_CHECKED) { len = recv(pThis->mSocket[0], buf, sizeof(buf), 0); } else { sockaddr_in from; int fromLen = sizeof(from); len = recvfrom(pThis->mSocket[0], buf, sizeof(buf), 0, (sockaddr*)&from, &fromLen); } // 原有数据处理逻辑保持不变... if (len > 0) { CString displayText; if (pThis->mRadioHex[0].GetCheck() == BST_CHECKED) { for (int i = 0; i < len; i++) { CString hex; hex.Format(_T("%02X "), (BYTE)buf[i]); displayText += hex; } } else { displayText = CString(buf, len); } // 跨线程更新UI pThis->PostMessage(WM_DISPLAY_DATA, len, (LPARAM)new CString(displayText)); } else if (len == 0) { // TCP连接关闭 pThis->PostMessage(WM_CONNECTION_CLOSED); break; } else if (len == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { // 忽略非阻塞模式下的预期错误 pThis->PostMessage(WM_SOCKET_ERROR, err); break; } } } } return 0; }VS2017 MFC 网络通信线程函数像兼容2个网口通信,怎么修改线程函数?
最新发布
10-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值