<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0

    現(xiàn)象

    用多線程方法設(shè)計(jì)socket程序時(shí),你會(huì)發(fā)現(xiàn)在跨線程使用CAsyncSocket及其派生類(lèi)時(shí),會(huì)出現(xiàn)程序崩潰。所謂跨線程,是指該對(duì)象在一個(gè)線程中 調(diào)用Create/AttachHandle/Attach函數(shù),然后在另外一個(gè)線程中調(diào)用其他成員函數(shù)。下面的例子就是一個(gè)典型的導(dǎo)致崩潰的過(guò)程:
    CAsyncSocket Socket;
    UINT Thread(LPVOID)
    {
    Socket.Close ();
    return 0;
    }
    void CTestSDlg::OnOK()
    {
    // TODO: Add extra validation here
    Socket.Create(0);
    AfxBeginThread(Thread,0,0,0,0,0);
    }

    其中Socket對(duì)象在主線程中被調(diào)用,在子線程中被關(guān)閉。

    跟蹤分析

    這個(gè)問(wèn)題的原因可以通過(guò)單步跟蹤(F11)的方法來(lái)了解。我們?cè)赟ocket.Create(0)處設(shè)斷點(diǎn),跟蹤進(jìn)去會(huì)發(fā)現(xiàn)下面的函數(shù)被調(diào)用:

    void PASCAL CAsyncSocket::AttachHandle(
    SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)
    {
    _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
    BOOL bEnable = AfxEnableMemoryTracking(FALSE);
    if (!bDead)
    {
    ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) == NULL);
    if (pState->m_pmapSocketHandle->IsEmpty())
    {
    ASSERT(pState->m_pmapDeadSockets->IsEmpty());
    ASSERT(pState->m_hSocketWindow == NULL);
    CSocketWnd* pWnd = new CSocketWnd;
    pWnd->m_hWnd = NULL;
    if (!pWnd->CreateEx(0, AfxRegisterWndClass(0),
    _T("Socket Notification Sink"),
    WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
    {
    TRACE0("Warning: unable to create socket notify window!"n");
    AfxThrowResourceException();
    }
    ASSERT(pWnd->m_hWnd != NULL);
    ASSERT(CWnd::FromHandlePermanent(pWnd->m_hWnd) == pWnd);
    pState->m_hSocketWindow = pWnd->m_hWnd;
    }
    pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
    }
    else
    {
    int nCount;
    if (pState->m_pmapDeadSockets->Lookup((void*)hSocket, (void*&)nCount))
    nCount++;
    else
    nCount = 1;
    pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount);
    }
    AfxEnableMemoryTracking(bEnable);
    }

    在這個(gè)函數(shù)的開(kāi)頭,首先獲得了一個(gè)pState的指針指向_afxSockThreadState對(duì)象。從名字可以看出,這似乎是一個(gè)和線程相關(guān)的變量,實(shí)際上它是一個(gè)宏,定義如下:

    #define _afxSockThreadState AfxGetModuleThreadState()

    我們沒(méi)有必要去細(xì)究這個(gè)指針的定義是如何的,只要知道它是和當(dāng)前線程密切關(guān)聯(lián)的,其他線程應(yīng)該也有類(lèi)似的指針,只是指向不同的結(jié)構(gòu)。

    在這個(gè)函數(shù)中,CAsyncSocket創(chuàng)建了一個(gè)窗口,并把如下兩個(gè)信息加入到pState所管理的結(jié)構(gòu)中:

        pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);
    pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount);
    pState->m_hSocketWindow = pWnd->m_hWnd;
    pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket);

    當(dāng)調(diào)用Close時(shí),我們?cè)俅胃櫍蜁?huì)發(fā)現(xiàn)在KillSocket中,下面的函數(shù)出現(xiàn)錯(cuò)誤:

        void PASCAL CAsyncSocket::KillSocket(SOCKET hSocket, CAsyncSocket* pSocket)
    {
    ASSERT(CAsyncSocket::LookupHandle(hSocket, FALSE) != NULL);

    我們?cè)谶@個(gè)ASSERT處設(shè)置斷點(diǎn),跟蹤進(jìn)LookupHandle,會(huì)發(fā)現(xiàn)這個(gè)函數(shù)定義如下:

    CAsyncSocket* PASCAL CAsyncSocket::LookupHandle(SOCKET hSocket, BOOL bDead)
    {
    CAsyncSocket* pSocket;
    _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
    if (!bDead)
    {
    pSocket = (CAsyncSocket*)
    pState->m_pmapSocketHandle->GetValueAt((void*)hSocket);
    if (pSocket != NULL)
    return pSocket;
    }
    else
    {
    pSocket = (CAsyncSocket*)
    pState->m_pmapDeadSockets->GetValueAt((void*)hSocket);
    if (pSocket != NULL)
    return pSocket;
    }
    return NULL;
    }

    顯然,這個(gè)函數(shù)試圖從當(dāng)前線程查詢(xún)關(guān)于這個(gè) socket的信息,可是這個(gè)信息放在創(chuàng)建這個(gè)socket的線程中,因此這種查詢(xún)顯然會(huì)失敗,最終返回NULL。

    有人會(huì)問(wèn),既然它是ASSERT出錯(cuò),是不是Release就沒(méi)問(wèn)題了。這只是自欺欺人。ASSERT/VERIFY都是檢驗(yàn)一些程序正常運(yùn)行必須正確的條件。如果ASSERT都失敗,在Release中也許不會(huì)顯現(xiàn),但是你的程序肯定運(yùn)行不正確,啥時(shí)候出錯(cuò)就不知道了。

    如何在多線程之間傳遞socket

    有些特殊情況下,可能需要在不同線程之間傳遞socket。當(dāng)然我不建議在使用CAsyncSOcket的時(shí)候這么做,因?yàn)檫@增加了出錯(cuò)的風(fēng)險(xiǎn)(尤其當(dāng)出現(xiàn)拆解包問(wèn)題時(shí),有人稱(chēng)為粘包,我基本不認(rèn)同這種稱(chēng)呼)。如果一定要這么做,方法應(yīng)該是:

    1. 當(dāng)前擁有這個(gè)socket的線程調(diào)用Detach方法,這樣socket句柄和C++對(duì)象及當(dāng)前線程脫離關(guān)系
    2. 當(dāng)前線程把這個(gè)對(duì)象傳遞給另外一個(gè)線程
    3. 另外一個(gè)線程創(chuàng)建新的CAsyncSocket對(duì)象,并調(diào)用Attach

    上面的例子,我稍微做修改,就不會(huì)出錯(cuò)了:

    CAsyncSocket Socket;
    UINT Thread(LPVOID sock)
    {
    Socket.Attach((SOCKET)sock);
    Socket.Close ();
    return 0;
    }
    void CTestSDlg::OnOK()
    {
    // TODO: Add extra validation here
    Socket.Create(0);
    SOCKET hSocket = Socket.Detach ();
    AfxBeginThread(Thread,(LPVOID)hSocket,0,0,0,0);
    }
    posted on 2008-07-03 15:22 SIMONE 閱讀(838) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): C++
    主站蜘蛛池模板: 亚洲精品天堂成人片?V在线播放| 久久综合九色综合97免费下载| 成年午夜视频免费观看视频 | 免费A级毛片无码久久版| 免费无码又爽又刺激毛片| 精品免费视在线观看| 亚洲av色香蕉一区二区三区| 久久精品亚洲综合专区| 亚洲av一综合av一区| 久久亚洲精品人成综合网| 亚洲?V乱码久久精品蜜桃 | 国产亚洲综合精品一区二区三区| 亚洲欧美一区二区三区日产| 亚洲中文字幕无码一去台湾| 久久亚洲AV成人无码国产| 久久久国产精品亚洲一区| 亚洲中文字幕在线无码一区二区| 亚洲精品蜜桃久久久久久| 亚洲夜夜欢A∨一区二区三区 | 毛片大全免费观看| 国产精品区免费视频| 免费国产黄网站在线观看视频| 亚洲成人免费电影| 在线观看视频免费国语| 四虎影视永久免费观看| 国产亚洲精久久久久久无码77777| 在线日韩av永久免费观看| 亚洲成人国产精品| 免费观看国产小粉嫩喷水| 亚洲免费日韩无码系列| 久久久久亚洲精品无码系列| 中文字幕在线亚洲精品| 亚洲国产第一页www| 亚洲精品无码乱码成人| 在线观看亚洲一区二区| 亚洲国产精品自在在线观看| 亚洲高清在线mv| 色九月亚洲综合网| 国产在线播放线91免费| 黄在线观看www免费看| 浮力影院第一页小视频国产在线观看免费|