<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

    現象

    用多線程方法設計socket程序時,你會發現在跨線程使用CAsyncSocket及其派生類時,會出現程序崩潰。所謂跨線程,是指該對象在一個線程中 調用Create/AttachHandle/Attach函數,然后在另外一個線程中調用其他成員函數。下面的例子就是一個典型的導致崩潰的過程:
    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對象在主線程中被調用,在子線程中被關閉。

    跟蹤分析

    這個問題的原因可以通過單步跟蹤(F11)的方法來了解。我們在Socket.Create(0)處設斷點,跟蹤進去會發現下面的函數被調用:

    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);
    }

    在這個函數的開頭,首先獲得了一個pState的指針指向_afxSockThreadState對象。從名字可以看出,這似乎是一個和線程相關的變量,實際上它是一個宏,定義如下:

    #define _afxSockThreadState AfxGetModuleThreadState()

    我們沒有必要去細究這個指針的定義是如何的,只要知道它是和當前線程密切關聯的,其他線程應該也有類似的指針,只是指向不同的結構。

    在這個函數中,CAsyncSocket創建了一個窗口,并把如下兩個信息加入到pState所管理的結構中:

        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);

    當調用Close時,我們再次跟蹤,就會發現在KillSocket中,下面的函數出現錯誤:

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

    我們在這個ASSERT處設置斷點,跟蹤進LookupHandle,會發現這個函數定義如下:

    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;
    }

    顯然,這個函數試圖從當前線程查詢關于這個 socket的信息,可是這個信息放在創建這個socket的線程中,因此這種查詢顯然會失敗,最終返回NULL。

    有人會問,既然它是ASSERT出錯,是不是Release就沒問題了。這只是自欺欺人。ASSERT/VERIFY都是檢驗一些程序正常運行必須正確的條件。如果ASSERT都失敗,在Release中也許不會顯現,但是你的程序肯定運行不正確,啥時候出錯就不知道了。

    如何在多線程之間傳遞socket

    有些特殊情況下,可能需要在不同線程之間傳遞socket。當然我不建議在使用CAsyncSOcket的時候這么做,因為這增加了出錯的風險(尤其當出現拆解包問題時,有人稱為粘包,我基本不認同這種稱呼)。如果一定要這么做,方法應該是:

    1. 當前擁有這個socket的線程調用Detach方法,這樣socket句柄和C++對象及當前線程脫離關系
    2. 當前線程把這個對象傳遞給另外一個線程
    3. 另外一個線程創建新的CAsyncSocket對象,并調用Attach

    上面的例子,我稍微做修改,就不會出錯了:

    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 閱讀(837) 評論(0)  編輯  收藏 所屬分類: C++
    主站蜘蛛池模板: 在线观看免费视频一区| 一二三四影视在线看片免费 | 久久亚洲精品国产精品黑人| 亚洲网址在线观看| 黄色免费在线网址| 国产精品69白浆在线观看免费| 精品亚洲成AV人在线观看| 黄色毛片免费在线观看| 免费一级e一片在线播放| 亚洲成a人片7777| 最近中文字幕免费大全| 国产亚洲一区二区手机在线观看 | 亚洲一区AV无码少妇电影| 黄网站免费在线观看| www国产亚洲精品久久久日本| 亚洲成年人电影在线观看| 真人做A免费观看| 久久精品国产亚洲一区二区| 国产精品亚洲精品爽爽| 色窝窝免费一区二区三区| 亚洲高清有码中文字| 无码国产精品一区二区免费式直播| 亚洲乳大丰满中文字幕| 看全免费的一级毛片| 久久伊人亚洲AV无码网站| 美女被爆羞羞网站免费| 红杏亚洲影院一区二区三区| 久久久久久成人毛片免费看| 国产亚洲精品AA片在线观看不加载 | 最好免费观看高清在线| 亚洲午夜AV无码专区在线播放| 亚洲精品无码国产片| 24小时日本在线www免费的| 亚洲成无码人在线观看| 在线观看亚洲免费视频| 亚洲乱码日产精品一二三| 久久国产成人亚洲精品影院 | 可以免费看的卡一卡二| 高h视频在线免费观看| 亚洲精品国产成人专区| 久别的草原电视剧免费观看|