之所以有本系列的分析,是因为两点:
- FileZilla 是目前非常火爆的开源ftp项目,整个项目采用C++代码编写,代码紧凑可读性高,值得学习(缺陷是注释太少)。
- 网络上已有的对该源码的分析基于的版本是0.9.18,分析比较粗略,无论是框架还是细节。
这里仅仅是我个人对FileZilla Server源码0.9.34版本的分析,能力有限,不足和错误之处还希望大家不吝斧正。本片作为开篇,略过如何编译(该源码源码用VS2010编译),如何配置,如何使用。FileZilla官网提供了程序和源码下载(源码包含在程序中,安装时默认为不安装),以及编译步骤和注意事项,感兴趣的朋友可以自行去官网寻找或google。
感谢:分析时参考了网友的系列文章《
FileZilla FTP服务器源代码分析》,大家可以参照比对。
首先预览一下源码目录source文件夹下的大致文件布局。
6个子目录,核心的代码(线程、socket、命令等)都放在当前目录下。6个子目录及对应代码功能:
子目录
|
功能
|
includes
|
当前版本下只有一个子目录openssl,看名识意,不多解释
|
install
|
安装脚本和资源
|
interface
|
界面UI实现类
|
misc
|
混杂类,比较重要的如md5,StdString等
|
res
|
程序编译资源,目前只有一个icon
|
tinyxml
|
著名的一款基于DOM模型小巧开源的xml解析器
|
当前source目录下源码按实现功能大致又分为以下几种类型:
功能分类
|
包括的文件
|
网络
|
全体文件名含socket的,Server.*,
|
线程
|
文件名包含Thread的文件
|
辅助
|
version.*,MFC64bitFix.*,conversion.*,config.h,service.cpp等除去网络和线程的文件 |
文件目录结构分析完了,面对众多.h.cpp文件,需要做一些去繁取精的操作。从无关紧要的地方开始,例如version.*。
version.*中声明定义了一个函数
CStdString GetVersionString(),需要注意的就是CStdString这个类,它的实现在misc/stdString.h文件中,这个类文件较大,功能稍后部分再分析。说句实话,这个函数是很值得收藏的。
Thread.*定义了线程类CThread,只需要注意那个Run函数中对线程消息做了处理,有用的消息交由虚函数
OnThreadMessage处理。
作为Visual Studio生成的C++代码中最常出现的两个文件stdafx.h和stdafx.cpp,我们势必需要首先弄清楚它们到底包含了哪些头文件,定义了哪些宏,什么了哪些函数以及结构体。
stdafx.h中包含了自己的config.h这个文件,顺便看一下这个文件的作用,代码很少目的有两个,强制使用unicode编译和检测是否安装了最新SDK。还包含了MFC64bitFix.h这个文件,也跟进去看看。定义了一个存储文件属性的结构体
CFileStatus64,以及操作它的若干全局函数,这个文件名有点怪,和包含的功能不匹配。
第55行遇到了条件宏
#ifdef MMGR,编译条件中有定义,包含misc/mmgr.h文件。mmgr是用于管理和跟踪内存的代码,之后会重点详细分析。
conversion.h中声明的函数用于ANSI和UTF8字符的互相转换,不多解释。
AsyncSocketEx.h中实现了异步socket,之后的ControlSocket,AdminListenSocket等文件中什么的socket都是由CAsyncSocketEx类派生来的,之后分析。
至此,stdafx.h中头文件包含全部结束,下面就是宏定义了。
先补充一个知识点,各消息的值范围和作用见下图:
注册了
WM_FILEZILLA_THREADMSG消息用来线程之
间通信,定义了
WM_FILEZILLA_SERVERMSG用于进
程间通信,即FileZilla server.exe和FileZilla Server Interface.exe。
这里仅贴出两处源码中调用这两个消息的例子,便可得知后面定义的几个常数宏的用处。
//ControlSocket.cpp第400行
SendStatus(_T("could not send reply, disconnected."), 0);
m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
//Server.cpp第813行器
int index = GetNextThreadNotificationID();
CServerThread *pThread = new CServerThread(WM_FILEZILLA_SERVERMSG + index);
m_ThreadNotificationIDs[index] = pThread;
从上面代码可以看出
PostThreadMessage的第二个参数wParam就是定义的数字宏,第三个参数是结构t_statusmsg,这些宏功能分别是:
FSM_STATUSMESSAGE:在管理窗口或log中显示并记录状态信息
FSM_CONNECTIONDATA:和连接相关的信息,如新用户连接,登录,退出等
FSM_THREADCANQUIT:退出线程
FSM_SEND:发送数据时用于管理窗口统计发送字节数
FSM_RECV:接受数据时用于管理窗口统计接收字节数
其余的就不多写了,宏名比较直观的显示出意思。
在往下定义了一系列的结构如
t_statusmsg,之后用到的地方在详述,知道这些结构在哪个文件中定义的就行了。
接着就是extern HWND hMainWnd;
这个外联的句柄就是下一节将要提到的CServer的窗口类句柄。
最后定义了一个CCriticalSectionWrapper类和两个帮助检测临界区死锁的函数,尤其是前者,DEBUG版本时错误的使用将导致当前线程挂起。
SpeedLimit.*: 速度限制(包括时间段限制)
这里针对UI性比较强,FillBuffer这个函数将所有限制条件格式化成一个char字符串,ParseBuffer则是解析这个字符串,采用这个
类,可以轻松实现强大的自定义限速功能。
defs.h:
这个类定义了服务器的状态,如在线、离线、锁住
等。
Options.*,OptionTypes.h
OptionTypes.h中定义了一个结构数组
m_Optinons,保存所有配置项信息,如是否使用SSL,同时在线最大用户数量,上传下载限速等等,所有这些大部分都被使用在Option那个对话框UI上。
t_option结构中有一个BOOL bOnlyLocal成员用于标示该项是否可以仅能够被本地连接修改,数组中只有最后两项Server name 和 server display name为TRUE,Options类就是操作配置文件的实体类(注意,它使用了tinyXML),服务器的配置文件存储在exe同级目录下,叫FileZilla Srver.xml。Options的主要操作是针对内存中的配置,只有与默认值不同的项才会存入配置文件中。
Options还有一个隐藏的friend窗体类
COptionsHelperWindow,定义在cpp文件中,这个类用于通过用post WM_USER给窗体消息这种异步的方式去更新option实例,而不是options类自身。
有了Options类和OptionTypes.h中定义的配置类型,就可以通过诸如 m_pOptions->GetOptionVal(OPTION_ENABLELOGGING)这样的方法方便的获取到配置。
FileLogger.* 日志
这个类中包含Options类的一个对象指针,用来读取日志文件的相关配置。
iputils.* 判断IP合法性以及是否处于某个过滤范围
它采用了大名鼎鼎的boost库的regex来判断,这个库之后有时间一定要好好研究一下。
autobanmanager.* 阻止用户继续登录的方法类文件
AutoBan这个设置项是一个非常浪费资源的,因为它对每一个失败的ip都要记录查询内存中的两个map。
Accounts.* 账户
Accounts.h中声明了3个类,t_directory,t_group,还有继承于t_group的t_user。
t_directory仅仅含有一些权限声明,相当于一个struct,被t_group和t_user使用。
剩余两个类主要做的事是对配置的读取分析,所有的数据都是基于字符串的。
permission.* 对用户、群组访问资源进行鉴权
权限配置信息记录在FileZilla Server.xml中。
服务器对每一个group和user都有权限限制,group权限优先于user权限,在
CheckFilePermissions 函数中可以看出。
conversion.* utf8和ansi字符的相互转化
ExternalIpCheck.* PASV模式
根据配置获取ip。
所有辅助文件已经分析完毕,下级节开始分析socket和线程类。