本节的分析是基于本系列第二篇FileZilla Server源码分析(2)之上,严格意义上来说是更为详细的分析,深入了解CAsyncSocketEx的实现,我将挑出重要的函数一一分析。
函数名都为红色粗体,并且带一对小括号,如果括号不含有字符“...”表示该函数无参数,否则有参数,具体什么参数不具体指明。变量均为黑色粗体。
//
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;
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;
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
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;
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 ;
}
// }
// {
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类的一个另类剖析吧。