Posted on 2010-06-03 17:31
乱78糟 阅读(2552)
评论(0) 编辑 收藏 引用 所属分类:
开源
上一节讲述的基本都是些做辅助的代码,本节分析诸多socket类的父类CAsyncSocketEx和相关的Layer类。
PS:拼音打字错别字很多- -。
从CAsyncSocketEx和CAsyncSocketExLayer类文件开头注释部分写到:
如何使用?-----------和MFC的CAsyncSocket非常像,如果不需要强化CAsyncSocket,那么在需要使用的时候只需替换掉CAsyncSocket即可。为什么这个类快一些?-------------------CAsyncSocketEx只是在分发通知事件消息的时候稍微快一点。首先来了解一下CAsyncSocket是如何工作的。对每个线程使用CAsyncSocket就会相应有一个窗口被创建。CAsyncSocket利用那个窗口的句柄调用WSAsyncSocket 。直到这儿,CAsyncSocket和它的工作方式是一样的。但是CAsyncSocket对一个线程中的所有sockets仅使用一个windows消息(WM_SOCKET_NOTIFY)。当这个窗口收到 WM_SOCKET_NOTIFY 时,wParam参数包含socket句柄并且这个窗口使用map来查找一个CAsyncSocket实例。CAsyncSocketEx原理不同于此。它的辅助窗口(helper window)使用一定范围内不同的window消息(WM_USER到OXBFFF)并且对每个socket传递不同的消息给WSAAsyncSelect,当这个指定范围内消息被接收的时候,CAsyncSocketEx使用这个消息的索引减去WM_USER的值配合指向
CAsyncSocketEx实例数组的指针来查找。如你所见,CAsyncSocketEx以更加高效的方式来使用辅助窗口,因为它不需要使用缓慢的maps来查找自己的实例。然后,速度增加的并不多,但是当同时使用大量的sockets的时候它的效果可能很明显。请注意这个变动并没有影响到原始数据吞吐效率,CAsyncSocketEx仅仅是分发通知消息的时候更加快速而已。CAsyncSocketEx还提供了什么?---------------------------CAsyncSocketEx提供了一个灵活的层系统。一个例子就是代理层。创建一个代理层实例,配置并将其加入到CAsyncSocketEx实例的层链(layer chain),之后,你就能够通过代理进行连接。好处:你不需要做很多变动就可以使用层系统。另一个层就是当前正在开发(注:目前已经完成,作者忘记修正这个注释了)的SSL层用来进行SSL加密连接。从作者的注释中我们可以大致了解这些类的功能和原理,源码我也仅挑若干个人比较感兴趣的部分稍微分析一下。
CAsyncSocketEx类和cAsyncSocketExLayer类互为友元类。
CAsyncSocketEx类中有大量的
#ifndef NOLAYERS ... #endif ,FileZillaServer工程中没有定义NOLAYERS,所以这些代码都要被编译。
#ifndef NOLAYERS
//Layer chain
CAsyncSocketExLayer *m_pFirstLayer;
CAsyncSocketExLayer *m_pLastLayer;
friend CAsyncSocketExLayer;
//Called by the layers to notify application of some events
virtual int OnLayerCallback(std::list<t_callbackMsg>& callbacks);
#endif //NOLAYERS
上面那一段代码是为了构造了作者注释中所说的层链,每个CAsyncSocketEx实例可以通过调用
AddLayer 函数添加一个层。
成员变量m_pendingCallbacks保存的是所有等待被调用的回调信息。当
WindowProc
参数message等于WM_USER+2的时候,调用
OnLayerCallback,
CAsyncSocketEx该虚成员函数仅做了清理工作,实际任务需要派生类去派生处理。
相对于FD_READ作者又定义了#define FD_FORCEREAD (1<<15)
来跳过检测是否有数据等待。现在用伪代码描述一下
WindowProc函数的工作:
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message>=WM_SOCKETEX_NOTIFY)
{
//根据message-(WM_USER+3)的值查找socket,WM_USER+3 == WM_SOCKETEX_NOTIFY
if (!pSocket->m_pFirstLayer)
//分发通知消息,例如FD_READ,FD_CONNECT等
else
//分发通知消息给最底层,即 pSocket->m_pLastLayer->CallEvent(nEvent, nErrorCode);
}
else if (message == WM_USER)
//处理某一层发送的通知事件
else if (message == WM_USER+1)
//通知连接的状态,即调用虚函数OnConnect
else if (message == WM_USER + 2)
//处理等待的回调信息
else if (message == WM_TIMER)
//这种情况因为收到FD_CLOSE事件时仍然有数据未读取,导致调用 pSocket->ResendCloseNotify()重发关闭消息,这个函数启动了定时器。
//重发FD_CLOSE消息通知socket关闭
}
CAsyncSocketEx类中虚函数,如
OnAccept,
OnSend等
On打头的函数用于通知特定事件的发生状态,派生类如果需要获得这些状态信息,就可以自己派生这些函数。
全局的
m_spAsyncSocketExThreadDataList则定义了一个
t_AsyncSocketExThreadData(即分发线程)的链表,也就是说FileZilla可以有多个分发线程,每个分发线程对应多个socket,即CAsyncSocketEx。
举一个实际的场景:
在FileZillaServer启动时,缺省监听了两个端口:21和admin端口,因此就有两个socket,即两个CAsyncSocketEx。这两个CAsyncSocketEx共用一个分发线程:
t_AsyncSocketExThreadData。
当有用户通过FTP连接上server并通过get/mget命令下载文件时,这时FTP服务器会启动一个传输线程在一个临时端口进行监听,这时会增加一个CAsyncSocketEx,同时也增加一个负责这个CAsyncSocketEx的分发线程,因此
m_spAsyncSocketExThreadDataList里也会增加一个结点。
这时的状况是:一个m_spAsyncSocketExThreadDataList链,两个t_AsyncSocketExThreadData,三个 CAsyncSocketEx。
下一节分析核心代码。