本節的分析是基于本系列第二篇FileZilla Server源碼分析(2)之上,嚴格意義上來說是更為詳細的分析,深入了解CAsyncSocketEx的實現,我將挑出重要的函數一一分析。
函數名都為紅色粗體,并且帶一對小括號,如果括號不含有字符“...”表示該函數無參數,否則有參數,具體什么參數不具體指明。變量均為黑色粗體。
首先來看一下該類的構造函數
CAsyncSocketEx(),構造函數完成的是部分成員變量的初始化工作,其中最重要的是一個結構體變量
m_SocketData,它的原型為:
//Strucure to hold the socket data
struct t_AsyncSocketExData
{
SOCKET hSocket; //Socket handle
int nSocketIndex; //Index of socket, required by CAsyncSocketExHelperWindow
int nFamily;
addrinfo *addrInfo, *nextAddr; // Iterate through protocols on connect failure
bool onCloseCalled; // Set to true on first received OnClose event
} m_SocketData;
還有
m_pLocalAsyncSocketExThreadData的原型為:
//Pointer to the data of the local thread
struct t_AsyncSocketExThreadData
{
CAsyncSocketExHelperWindow *m_pHelperWindow;
int nInstanceCount;
DWORD nThreadId;
std::list<CAsyncSocketEx*> layerCloseNotify;
} *m_pLocalAsyncSocketExThreadData;
每個成員具體作用注釋已經比較清楚地說明了,后面用到的時候再指出。除了層
NOLAYERS編譯(如果不明白,請看第二篇)此外還有一個宏條件編譯需要注意
#ifndef NOSOCKETSTATES
m_nPendingEvents = 0; //socket當前未決的網絡事件,例如FD_READ
m_nState = notsock; //socket當前狀態
#endif //NOSOCKETSTATES
析構函數
~CAsyncSocketEx()調用函數
Close()關閉socket,并調用
FreeAsyncSocketExInstance()做清理工作。
Close()函數中關閉層m_pFirstLayer->Close(),之后關閉成員變量
m_SocketData.hSocket并且從輔助窗口
m_pLocalAsyncSocketExThreadData->m_pHelperWindow記錄中移除掉這個socket,之后就是銷毀各種資源如地址、代理層等,有一個細節,不明白的可以MSDN,不細說了。
if (m_hAsyncGetHostByNameHandle)
WSACancelAsyncRequest(m_hAsyncGetHostByNameHandle);
m_hAsyncGetHostByNameHandle = NULL;
再說
FreeAsyncSocketExInstance()之前先說對應的函數InitAsyncSocketExInstance(),這兩個函數干的活都和一個static變量m_spAsyncSocketExThreadDataList有關,一個初始化,一個銷毀,m_pLocalAsyncSocketExThreadData保存了當前線程的id和輔助窗口的指針。
Create(...)函數創建代理層或者自身的socket以及做綁定到輔助窗口等操作。如果定義了使用代理層,那么所有關于socket的操作都會被代理層攔截,如create,listen,connect,accpet,recv,send,但是不包括bind,因為代理層create的時候已經提前綁定過了。
TriggerEvent(...)這個函數用來觸發程序員指定的網絡事件,例如CControlSocket類中的
Send(...)函數就調用了
TriggerEvent(FD_WRITE)來觸發寫操作。它通過PosetMessage給輔助窗口,然后窗口通過消息處理函數
WindowProc(...)處理這種種消息(詳細請
參考第二節)。
與代理層相關的函數,如
AddLayer(...),
RemoveAllLayers()等,還有設置獲取各種信息的函數如GetSockOpt()就不在詳述了。
下面再補充之前函數
WindowProc(...)關于網絡事件的詳細處理,僅僅針對非代理層的處理:
//if (!pSocket->m_pFirstLayer)
//{
switch (nEvent)
{
case FD_READ:
if (pSocket->GetState() == connecting && !nErrorCode)
{
pSocket->m_nPendingEvents |= FD_READ; //如果正在連接,那么將讀事件加入未決事件變量里
break;
}
else if (pSocket->GetState() == attached)//已綁定成功的設置為連接成功
pSocket->SetState(connected);
if (pSocket->GetState() != connected) //如果還沒有連接成功,跳出
break;
// Ignore further FD_READ events after FD_CLOSE has been received
if (pSocket->m_SocketData.onCloseCalled)
break;
if (pSocket->m_lEvent & FD_READ)
{
DWORD nBytes = 0;
if (!nErrorCode)
if (!pSocket->IOCtl(FIONREAD, &nBytes)) //獲取要可讀的字節數
nErrorCode = WSAGetLastError();
if (nErrorCode)
pSocket->SetState(aborted); //出錯
if (nBytes != 0 || nErrorCode != 0) //通知socket已經有數據可以讀了
pSocket->OnReceive(nErrorCode);
}
break;
case FD_FORCEREAD:
//除了不用獲取去可讀的字節數之外,完全可FD_READ一樣,這是作者自定義的類型
break;
case FD_WRITE:
//前面的狀態判斷和FD_READ類似,不再詳述
if (pSocket->m_lEvent & FD_WRITE)
{
if (nErrorCode)
pSocket->SetState(aborted);
pSocket->OnSend(nErrorCode);//通知socket已經有數據可以發送了
}
break;
case FD_CONNECT:
if (pSocket->GetState() == connecting)
{
if (nErrorCode && pSocket->m_SocketData.nextAddr) //有多個地址?
{
if (pSocket->TryNextProtocol()) //嘗試下一個協議地址
break;
}
pSocket->SetState(connected);
}
else if (pSocket->GetState() == attached && !nErrorCode)
pSocket->SetState(connected);
if (pSocket->m_lEvent & FD_CONNECT)
pSocket->OnConnect(nErrorCode);
if (!nErrorCode)
{
//判斷未決事件中是否期望的讀寫事件,如果有,通知socket
if ((pSocket->m_nPendingEvents&FD_READ) && pSocket->GetState() == connected)
pSocket->OnReceive(0);
if ((pSocket->m_nPendingEvents&FD_FORCEREAD) && pSocket->GetState() == connected)
pSocket->OnReceive(0);
if ((pSocket->m_nPendingEvents&FD_WRITE) && pSocket->GetState() == connected)
pSocket->OnSend(0);
}
pSocket->m_nPendingEvents = 0;
break;
case FD_ACCPET:
//如果不是監聽或已經綁定狀態,跳出
if (pSocket->GetState() != listening && pSocket->GetState() != attached)
break;
if (pSocket->m_lEvent & FD_ACCEPT)
pSocket->OnAccept(nErrorCode);//通知
break;
case FD_CLOSE:
//沒有連接或綁定,跳出
if (pSocket->GetState() != connected && pSocket->GetState() != attached)
break;
// If there are still bytes left to read, call OnReceive instead of
// OnClose and trigger a new OnClose
DWORD nBytes = 0;
if (!nErrorCode && pSocket->IOCtl(FIONREAD, &nBytes))
{
//作者的注釋很清楚,如果關閉的時候還有數據可讀,將當前pSocket->m_SocketData.onCloseCalled 設置為TRUE
//以表示需要再一次調用關閉函數OnClose
if (nBytes > 0)
{
// Just repeat message.
PostMessage(hWnd, message, wParam, lParam);
pSocket->m_SocketData.onCloseCalled = true;
pSocket->OnReceive(WSAESHUTDOWN);
break;
}
}
pSocket->SetState(nErrorCode?aborted:closed);
pSocket->OnClose(nErrorCode);
break;
}
//}
本節是對第二節的一個小補充,也算是對MS的CAsyncSocket類的一個另類剖析吧。