• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            沒畫完的畫

            喂馬 劈柴 BBQ~
            posts - 37, comments - 55, trackbacks - 0, articles - 0
              C++博客 ::  :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            CAsyncSocket 的 OnReceive() 第一集

            Posted on 2008-09-10 08:48 沒畫完的畫 閱讀(5081) 評(píng)論(3)  編輯 收藏 引用 所屬分類: VC

            在應(yīng)用中,Client端 使用了 MFC 的  CAsyncSocket 跟 Server 通信
             
                      Client                                    Server
            CAsyncSocket::Send()           -->           |
            CAsyncSocket::OnReceive()    <--           |
             
            Client 使用 Send() 向 Server 端發(fā)送報(bào)文,Sever 端返回報(bào)文時(shí),會(huì)觸發(fā) OnReceive() 事件,告訴我們有數(shù)據(jù)到達(dá)了
             
            接收 Server 數(shù)據(jù)包的實(shí)現(xiàn)代碼如下:

            class CClientSocket : public CAsyncSocket
            {
            private:
                CBytesBuff m_buff;   
            // CBytesBuff  封裝了緩沖區(qū)的操作
            }
            ;
             
            #define RECV_BUFFER_SIZE 1024
             
            void CClientSocket::OnReceive(int nErrorCode)     
            {
                
            char szBuff[RECV_BUFFER_SIZE] = {0};    // 接收數(shù)據(jù)的緩沖區(qū)
                
                
            int nRead = 0;                                      // 真正接收數(shù)據(jù)的大小
                nRead = Receive(szBuff, sizeof(szBuff) );  // 接收數(shù)據(jù)
               
                
            switch (nRead)
                
            {
                
            case 0// 接收數(shù)據(jù)的長度返回0, 表示 Socket 已經(jīng)斷開
                    {
                        Close();
                        Notify_UI_SOCKET_CLOSE( ::WSAGetLastError() );
                    }
            ;
                    
            break;
             
                
            case SOCKET_ERROR: // 出錯(cuò)
                   {
                        
            int nErrCode = ::WSAGetLastError();
             
                        
            if ( nErrCode  != WSAEWOULDBLOCK) 
                       
            {
                           Close();
                           Notify_UI_SOCKET_CLOSE( nErrCode );
                       }

                  }

                  
            break;
             
                
            default// 正常情況
                    {
                        m_buff.append(szBuff, nRead);
             
                        
            while( m_buff.getDataSize() <= 0 )
                        
            {
                            
            // handleData() 返回已經(jīng)處理的數(shù)據(jù)長度, 然后從 m_buff 中移刪已經(jīng)處理的數(shù)據(jù)
                            
            // 直至 m_buff 中沒有數(shù)據(jù)
                            
            // handleData() 函數(shù)只有處理完 
                            int nHandledLen = handleData(m_buff.getData(), m_buff.getSize());
                            m_buff.popData(nHandledLen);
                        }

                    }


                }
            // switch

                CAsyncSocket::OnReceive(nErrorCode);
            }
               


            我總以為上段的代碼可以正常地運(yùn)行,但事與愿違
             
            當(dāng) Client端在處理 Server端 數(shù)據(jù)時(shí),有時(shí)在 handleData() 里調(diào)用了一個(gè) MessageBox() 時(shí),就會(huì)連續(xù)彈出兩個(gè) MessageBox
            經(jīng)過檢查,
             
            在 handleData() 中處理某一個(gè)報(bào)文時(shí)調(diào)用 MessageBox() 時(shí),如果 MessageBox() 不返回,那么 handleData() 自然也不會(huì)返回
            這里后面的 m_buff.popData(nHandledLen); 語句也無法執(zhí)行到, 那么數(shù)據(jù)永遠(yuǎn)在 m_buff 里無法移刪除,
            如果這時(shí) Server端 又再返回一個(gè) 數(shù)據(jù)包 ,
            CAsyncSocket 又會(huì)觸發(fā) OnReceive(),{請(qǐng)記住,這時(shí)上一個(gè) 數(shù)據(jù)包 還在 m_buff 里}
            所以 當(dāng)執(zhí)行到   int nHandledLen = handleData(m_buff.getData(), m_buff.getSize()); 時(shí)
            又會(huì)彈出一個(gè) MessageBox();
             
            導(dǎo)致的異常有
            1、調(diào)用 MessageBox() 的那段處理 數(shù)據(jù)包的 代碼會(huì)被執(zhí)行兩次
            2、m_buff.popData(nHandledLen); 這句代碼同樣會(huì)執(zhí)行兩次導(dǎo)致數(shù)據(jù)解析出錯(cuò)
             

            在我的應(yīng)用中,只允許處理完一個(gè) 數(shù)據(jù)包后才能處理下一個(gè) 數(shù)據(jù)包

            火星人都知道,當(dāng)有數(shù)據(jù)到達(dá)時(shí),CAsynSocket 是采用消息的方式來通知的

            BOOL CAsyncSocket::AsyncSelect(long lEvent)
            {
             ASSERT(m_hSocket 
            != INVALID_SOCKET);

             _AFX_SOCK_THREAD_STATE
            * pState = _afxSockThreadState;
             ASSERT(pState
            ->m_hSocketWindow != NULL);

             
            return WSAAsyncSelect(m_hSocket, pState->m_hSocketWindow,
              WM_SOCKET_NOTIFY, lEvent) 
            != SOCKET_ERROR;
            }


            從 CAsyncSocket 的實(shí)現(xiàn)來看,每一條線程 CAsyncSocket 都會(huì)創(chuàng)建一個(gè){隱形窗口},當(dāng)有網(wǎng)絡(luò)事件時(shí),
            會(huì)向這個(gè)窗口發(fā)送  WM_SOCKET_NOTIFY 消息

            BEGIN_MESSAGE_MAP(CSocketWnd, CWnd)
                
            //{{AFX_MSG_MAP(CWnd)
                ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify)
                ON_MESSAGE(WM_SOCKET_DEAD, OnSocketDead)
                
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()

             

            LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
            {
                CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
                CSocket::ProcessAuxQueue();
                
            return 0L;
            }

            請(qǐng)注意:上面的 CSocket 并非是 class CSocket : public CAsyncSocket, 而是另一個(gè)在MFC框架內(nèi)部的一個(gè)封裝.

            結(jié)論是: CAsyncSocket::OnConnect(), CAsyncSocket::OnRecive()...... 這些函數(shù)是在  WM_SOCKET_NOTIFY 的消息響應(yīng)
            函數(shù)里調(diào)用的

            Google到的東西
            AfxMessageBox()、MessageBox() 并不會(huì)阻塞消息隊(duì)列

            具體為什么 AfxMessageBox()、MessageBox() 不會(huì)阻塞到消息隊(duì)列,需要再詳細(xì)查找相關(guān)資料
            -- AfxMessageBox()、MessageBox() 會(huì)造成代碼的阻塞(因?yàn)槿绻?nbsp; MessageBox() 不返回,是不會(huì)執(zhí)行到 MessageBox() 之后的代碼)
                但它們并不會(huì)造成線程消息隊(duì)列的阻塞
               
                Google 到一些資料說的是,AfxMessageBox() 和 MessageBox() 會(huì)把主窗體Enable 并且 會(huì)有另一個(gè)消息循環(huán),
                雖然原來窗體的消息循環(huán)的 DispatchMessage() 沒有返回,但由于 AfxMessageBox() 和 MessageBox() 又有另一個(gè)消息循環(huán)
                所以,新的消息又會(huì)被響應(yīng)到

                作了一個(gè)簡(jiǎn)單的試驗(yàn), 寫一個(gè) Client 和 一個(gè) Server
                Client 在收到  Server 端的數(shù)據(jù)時(shí)就顯示一個(gè) MessageBox(),Server 向 Client 端發(fā)送兩次數(shù)據(jù)
                Client 端在第二次收到數(shù)據(jù)時(shí),設(shè)置斷點(diǎn),查看堆棧
               

            CTestMFCSocketClientDlg::show() line 178
            CClientSocket::OnReceive(
            int 0) line 61
            CAsyncSocket::DoCallBack(unsigned 
            int 3876long 1) line 530
            CSocket::ProcessAuxQueue() line 
            823
            CSocketWnd::OnSocketNotify(unsigned 
            int 3876long 1) line 1127
            CWnd::OnWndMsg(unsigned 
            int 883, unsigned int 3876long 1long * 0x0012e56c) line 1826 + 17 bytes
            CWnd::WindowProc(unsigned 
            int 883, unsigned int 3876long 1) line 1596 + 30 bytes
            AfxCallWndProc(CWnd 
            * 0x00374d20 {CSocketWnd hWnd=0x00110c92}, HWND__ * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 215 + 26 bytes
            AfxWndProc(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 379
            AfxWndProcBase(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 220 + 21 bytes
            USER32
            ! 77d48734()
            USER32
            ! 77d48816()
            USER32
            ! 77d489cd()
            USER32
            ! 77d48a10()
            USER32
            ! 77d5e2b9()
            USER32
            ! 77d561c6()
            USER32
            ! 77d6a92e()
            USER32
            ! 77d6a294()
            USER32
            ! 77d95fbb()
            USER32
            ! 77d96060()
            USER32
            ! 77d80577()
            USER32
            ! 77d8052f()
            CWinApp::DoMessageBox(
            const char * 0x004153e4 `string', unsigned int 48, unsigned int 0) line 113 + 25 bytes
            AfxMessageBox(const char * 0x004153e4 `string', unsigned int 0, unsigned int 0) line 131 + 26 bytes
            CTestMFCSocketClientDlg::show() line 179
            CClientSocket::OnReceive(
            int 0) line 61
            CAsyncSocket::DoCallBack(unsigned 
            int 3876long 1) line 530
            CSocket::ProcessAuxQueue() line 
            823
            CSocketWnd::OnSocketNotify(unsigned 
            int 3876long 1) line 1127
            CWnd::OnWndMsg(unsigned 
            int 883, unsigned int 3876long 1long * 0x0012f074) line 1826 + 17 bytes
            CWnd::WindowProc(unsigned 
            int 883, unsigned int 3876long 1) line 1596 + 30 bytes
            AfxCallWndProc(CWnd 
            * 0x00374d20 {CSocketWnd hWnd=0x00110c92}, HWND__ * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 215 + 26 bytes
            AfxWndProc(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 379
            AfxWndProcBase(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 220 + 21 bytes
            USER32
            ! 77d48734()
            USER32
            ! 77d48816()
            USER32
            ! 77d489cd()
            USER32
            ! 77d48a10()
            USER32
            ! 77d5e2b9()
            USER32
            ! 77d561c6()
            USER32
            ! 77d6a92e()
            USER32
            ! 77d6a294()
            USER32
            ! 77d95fbb()
            USER32
            ! 77d96060()
            USER32
            ! 77d80577()
            USER32
            ! 77d8052f()
            CWinApp::DoMessageBox(
            const char * 0x004153e4 `string', unsigned int 48, unsigned int 0) line 113 + 25 bytes
            AfxMessageBox(const char * 0x004153e4 `string', unsigned int 0, unsigned int 0) line 131 + 26 bytes
            CTestMFCSocketClientDlg::show() line 179
            CClientSocket::OnReceive(
            int 0) line 61
            CAsyncSocket::DoCallBack(unsigned 
            int 3876long 1) line 530
            CSocket::ProcessAuxQueue() line 
            823
            CSocketWnd::OnSocketNotify(unsigned 
            int 3876long 1) line 1127
            CWnd::OnWndMsg(unsigned 
            int 883, unsigned int 3876long 1long * 0x0012fb7c) line 1826 + 17 bytes
            CWnd::WindowProc(unsigned 
            int 883, unsigned int 3876long 1) line 1596 + 30 bytes
            AfxCallWndProc(CWnd 
            * 0x00374d20 {CSocketWnd hWnd=0x00110c92}, HWND__ * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 215 + 26 bytes
            AfxWndProc(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 379
            AfxWndProcBase(HWND__ 
            * 0x00110c92, unsigned int 883, unsigned int 3876long 1) line 220 + 21 bytes
            USER32
            ! 77d48734()
            USER32
            ! 77d48816()
            USER32
            ! 77d489cd()
            USER32
            ! 77d496c7()
            CWinThread::PumpMessage() line 
            853
            CWnd::RunModalLoop(unsigned 
            long 4) line 3489 + 19 bytes
            CDialog::DoModal() line 
            539 + 12 bytes
            CTestMFCSocketClientApp::InitInstance() line 
            65 + 8 bytes
            AfxWinMain(HINSTANCE__ 
            * 0x00400000, HINSTANCE__ * 0x00000000char * 0x00141f3dint 1) line 39 + 11 bytes
            WinMain(HINSTANCE__ 
            * 0x00400000, HINSTANCE__ * 0x00000000char * 0x00141f3dint 1) line 30
            WinMainCRTStartup() line 
            330 + 54 bytes
            KERNEL32
            ! 7c816fd7()

            從堆棧的情況來看,AfxMessageBox() 里面確實(shí)存在另一個(gè)消息循環(huán)在處理消息。
            但還未從 Microsoft 的MSDN上找到相關(guān)的官方資料來證實(shí)這一點(diǎn)。
            未解決

            所以一切清楚了,原因是即使 AfxMessageBox() 不返回,也不會(huì)影響到 OnRecive() 的調(diào)用

            目前想到的解決方法有兩個(gè),大概思路如下
            第一種:想辦法在{第二個(gè)消息循環(huán)}中過濾掉 CAsynSocket 的 WM_SOCKET_NOTIFY 消息,并將這些過濾掉的消息存放在一個(gè)隊(duì)列,等{第二個(gè)消息循環(huán)}結(jié)束后,再把剛剛保存在隊(duì)列里的消息放回消息隊(duì)列里,讓第一個(gè)消息循環(huán)去處理

            第二種:在 CAsynSocket 的 OnRecive() 里開線程,然后通過線程鎖來保證,在未處理完{Server端發(fā)送過來的報(bào)文}時(shí),如果Server端有新的報(bào)文過來,會(huì)阻塞在線程里

            Feedback

            # re: CAsyncSocket 的 OnReceive()[未登錄]  回復(fù)  更多評(píng)論   

            2008-09-10 19:53 by 漂舟
            用UI消息提示錯(cuò)誤正確,但不要用彈出式窗口。

            # re: CAsyncSocket 的 OnReceive()  回復(fù)  更多評(píng)論   

            2008-09-10 22:13 by 沒畫完的畫
            @漂舟

            用UI消息提示錯(cuò)誤正確,但不要用彈出式窗口。

            樓上,你說的“用UI消息提示錯(cuò)誤正確”是什么意思?

            # re: CAsyncSocket 的 OnReceive()[未登錄]  回復(fù)  更多評(píng)論   

            2008-09-10 22:44 by 漂舟
            事務(wù)失敗的消息,用戶有可能需要知道,
            所以必須在UI上有所提示、或顯示,或暫時(shí)放入隊(duì)列,用戶查看時(shí),可顯示,
            即這個(gè)思路我認(rèn)為是相當(dāng)正確的,
            在UI上作提示,這個(gè)過程最好是異步操作,
            除非是確定能迅速完成,才用同步。
            嫩草影院久久99| 国产亚洲综合久久系列| 久久99国产一区二区三区| 久久综合九色综合久99| 欧美精品国产综合久久| 久久精品国产精品青草app| 亚洲国产精品无码久久久久久曰 | 久久精品九九亚洲精品天堂| 国产偷久久久精品专区| 人人狠狠综合久久亚洲88| 久久久久久免费一区二区三区| 久久香蕉综合色一综合色88| 久久福利青草精品资源站免费| 91久久香蕉国产熟女线看| 综合久久精品色| 97香蕉久久夜色精品国产| 99久久精品费精品国产| 久久久久亚洲AV无码网站| 中文字幕无码av激情不卡久久| 久久se这里只有精品| 欧美激情精品久久久久| 国产精品一久久香蕉国产线看观看| 亚洲国产成人久久精品影视| 久久香综合精品久久伊人| 少妇人妻88久久中文字幕| 久久久精品人妻一区二区三区蜜桃| 香蕉久久久久久狠狠色| 亚洲综合久久夜AV | 欧美大战日韩91综合一区婷婷久久青草| 久久99精品久久久久子伦| 国产美女久久精品香蕉69| 久久久综合九色合综国产| 国产亚洲综合久久系列| 久久最近最新中文字幕大全| 久久国产成人午夜AV影院| 一本久道久久综合狠狠躁AV| 国产精品久久国产精品99盘 | 久久婷婷五月综合国产尤物app | 久久99免费视频| 亚洲精品成人网久久久久久| 精品久久久久久无码不卡|