本节的分析是基于本系列第二篇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类的一个另类剖析吧。