之所以有本系列的分析,是因?yàn)閮牲c(diǎn):
- FileZilla 是目前非常火爆的開(kāi)源ftp項(xiàng)目,整個(gè)項(xiàng)目采用C++代碼編寫,代碼緊湊可讀性高,值得學(xué)習(xí)(缺陷是注釋太少)。
- 網(wǎng)絡(luò)上已有的對(duì)該源碼的分析基于的版本是0.9.18,分析比較粗略,無(wú)論是框架還是細(xì)節(jié)。
這里僅僅是我個(gè)人對(duì)FileZilla Server源碼0.9.34版本的分析,能力有限,不足和錯(cuò)誤之處還希望大家不吝斧正。本片作為開(kāi)篇,略過(guò)如何編譯(該源碼源碼用VS2010編譯),如何配置,如何使用。FileZilla官網(wǎng)提供了程序和源碼下載(源碼包含在程序中,安裝時(shí)默認(rèn)為不安裝),以及編譯步驟和注意事項(xiàng),感興趣的朋友可以自行去官網(wǎng)尋找或google。
感謝:分析時(shí)參考了網(wǎng)友的系列文章《
FileZilla FTP服務(wù)器源代碼分析》,大家可以參照比對(duì)。
首先預(yù)覽一下源碼目錄source文件夾下的大致文件布局。

6個(gè)子目錄,核心的代碼(線程、socket、命令等)都放在當(dāng)前目錄下。6個(gè)子目錄及對(duì)應(yīng)代碼功能:
子目錄
|
功能
|
includes
|
當(dāng)前版本下只有一個(gè)子目錄openssl,看名識(shí)意,不多解釋
|
install
|
安裝腳本和資源
|
interface
|
界面UI實(shí)現(xiàn)類
|
misc
|
混雜類,比較重要的如md5,StdString等
|
res
|
程序編譯資源,目前只有一個(gè)icon
|
tinyxml
|
著名的一款基于DOM模型小巧開(kāi)源的xml解析器
|
當(dāng)前source目錄下源碼按實(shí)現(xiàn)功能大致又分為以下幾種類型:
功能分類
|
包括的文件
|
網(wǎng)絡(luò)
|
全體文件名含socket的,Server.*,
|
線程
|
文件名包含Thread的文件
|
輔助
|
version.*,MFC64bitFix.*,conversion.*,config.h,service.cpp等除去網(wǎng)絡(luò)和線程的文件 |
文件目錄結(jié)構(gòu)分析完了,面對(duì)眾多.h.cpp文件,需要做一些去繁取精的操作。從無(wú)關(guān)緊要的地方開(kāi)始,例如version.*。
version.*中聲明定義了一個(gè)函數(shù)
CStdString GetVersionString(),需要注意的就是CStdString這個(gè)類,它的實(shí)現(xiàn)在misc/stdString.h文件中,這個(gè)類文件較大,功能稍后部分再分析。說(shuō)句實(shí)話,這個(gè)函數(shù)是很值得收藏的。
Thread.*定義了線程類CThread,只需要注意那個(gè)Run函數(shù)中對(duì)線程消息做了處理,有用的消息交由虛函數(shù)
OnThreadMessage處理。
作為Visual Studio生成的C++代碼中最常出現(xiàn)的兩個(gè)文件stdafx.h和stdafx.cpp,我們勢(shì)必需要首先弄清楚它們到底包含了哪些頭文件,定義了哪些宏,什么了哪些函數(shù)以及結(jié)構(gòu)體。
stdafx.h中包含了自己的config.h這個(gè)文件,順便看一下這個(gè)文件的作用,代碼很少目的有兩個(gè),強(qiáng)制使用unicode編譯和檢測(cè)是否安裝了最新SDK。還包含了MFC64bitFix.h這個(gè)文件,也跟進(jìn)去看看。定義了一個(gè)存儲(chǔ)文件屬性的結(jié)構(gòu)體
CFileStatus64,以及操作它的若干全局函數(shù),這個(gè)文件名有點(diǎn)怪,和包含的功能不匹配。
第55行遇到了條件宏
#ifdef MMGR,編譯條件中有定義,包含misc/mmgr.h文件。mmgr是用于管理和跟蹤內(nèi)存的代碼,之后會(huì)重點(diǎn)詳細(xì)分析。
conversion.h中聲明的函數(shù)用于ANSI和UTF8字符的互相轉(zhuǎn)換,不多解釋。
AsyncSocketEx.h中實(shí)現(xiàn)了異步socket,之后的ControlSocket,AdminListenSocket等文件中什么的socket都是由CAsyncSocketEx類派生來(lái)的,之后分析。
至此,stdafx.h中頭文件包含全部結(jié)束,下面就是宏定義了。
先補(bǔ)充一個(gè)知識(shí)點(diǎn),各消息的值范圍和作用見(jiàn)下圖:

注冊(cè)了
WM_FILEZILLA_THREADMSG消息用來(lái)線程之
間通信,定義了
WM_FILEZILLA_SERVERMSG用于進(jìn)
程間通信,即FileZilla server.exe和FileZilla Server Interface.exe。
這里僅貼出兩處源碼中調(diào)用這兩個(gè)消息的例子,便可得知后面定義的幾個(gè)常數(shù)宏的用處。
//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的第二個(gè)參數(shù)wParam就是定義的數(shù)字宏,第三個(gè)參數(shù)是結(jié)構(gòu)t_statusmsg,這些宏功能分別是:
FSM_STATUSMESSAGE:在管理窗口或log中顯示并記錄狀態(tài)信息
FSM_CONNECTIONDATA:和連接相關(guān)的信息,如新用戶連接,登錄,退出等
FSM_THREADCANQUIT:退出線程
FSM_SEND:發(fā)送數(shù)據(jù)時(shí)用于管理窗口統(tǒng)計(jì)發(fā)送字節(jié)數(shù)
FSM_RECV:接受數(shù)據(jù)時(shí)用于管理窗口統(tǒng)計(jì)接收字節(jié)數(shù)
其余的就不多寫了,宏名比較直觀的顯示出意思。
在往下定義了一系列的結(jié)構(gòu)如
t_statusmsg,之后用到的地方在詳述,知道這些結(jié)構(gòu)在哪個(gè)文件中定義的就行了。
接著就是extern HWND hMainWnd;
這個(gè)外聯(lián)的句柄就是下一節(jié)將要提到的CServer的窗口類句柄。
最后定義了一個(gè)CCriticalSectionWrapper類和兩個(gè)幫助檢測(cè)臨界區(qū)死鎖的函數(shù),尤其是前者,DEBUG版本時(shí)錯(cuò)誤的使用將導(dǎo)致當(dāng)前線程掛起。
SpeedLimit.*: 速度限制(包括時(shí)間段限制)
這里針對(duì)UI性比較強(qiáng),F(xiàn)illBuffer這個(gè)函數(shù)將所有限制條件格式化成一個(gè)char字符串,ParseBuffer則是解析這個(gè)字符串,采用這個(gè)
類,可以輕松實(shí)現(xiàn)強(qiáng)大的自定義限速功能。
defs.h:
這個(gè)類定義了服務(wù)器的狀態(tài),如在線、離線、鎖住
等。
Options.*,OptionTypes.h
OptionTypes.h中定義了一個(gè)結(jié)構(gòu)數(shù)組
m_Optinons,保存所有配置項(xiàng)信息,如是否使用SSL,同時(shí)在線最大用戶數(shù)量,上傳下載限速等等,所有這些大部分都被使用在Option那個(gè)對(duì)話框UI上。
t_option結(jié)構(gòu)中有一個(gè)BOOL bOnlyLocal成員用于標(biāo)示該項(xiàng)是否可以僅能夠被本地連接修改,數(shù)組中只有最后兩項(xiàng)Server name 和 server display name為TRUE,Options類就是操作配置文件的實(shí)體類(注意,它使用了tinyXML),服務(wù)器的配置文件存儲(chǔ)在exe同級(jí)目錄下,叫FileZilla Srver.xml。Options的主要操作是針對(duì)內(nèi)存中的配置,只有與默認(rèn)值不同的項(xiàng)才會(huì)存入配置文件中。
Options還有一個(gè)隱藏的friend窗體類
COptionsHelperWindow,定義在cpp文件中,這個(gè)類用于通過(guò)用post WM_USER給窗體消息這種異步的方式去更新option實(shí)例,而不是options類自身。
有了Options類和OptionTypes.h中定義的配置類型,就可以通過(guò)諸如 m_pOptions->GetOptionVal(OPTION_ENABLELOGGING)這樣的方法方便的獲取到配置。
FileLogger.* 日志
這個(gè)類中包含Options類的一個(gè)對(duì)象指針,用來(lái)讀取日志文件的相關(guān)配置。
iputils.* 判斷IP合法性以及是否處于某個(gè)過(guò)濾范圍
它采用了大名鼎鼎的boost庫(kù)的regex來(lái)判斷,這個(gè)庫(kù)之后有時(shí)間一定要好好研究一下。
autobanmanager.* 阻止用戶繼續(xù)登錄的方法類文件
AutoBan這個(gè)設(shè)置項(xiàng)是一個(gè)非常浪費(fèi)資源的,因?yàn)樗鼘?duì)每一個(gè)失敗的ip都要記錄查詢內(nèi)存中的兩個(gè)map。
Accounts.* 賬戶
Accounts.h中聲明了3個(gè)類,t_directory,t_group,還有繼承于t_group的t_user。
t_directory僅僅含有一些權(quán)限聲明,相當(dāng)于一個(gè)struct,被t_group和t_user使用。
剩余兩個(gè)類主要做的事是對(duì)配置的讀取分析,所有的數(shù)據(jù)都是基于字符串的。
permission.* 對(duì)用戶、群組訪問(wèn)資源進(jìn)行鑒權(quán)
權(quán)限配置信息記錄在FileZilla Server.xml中。
服務(wù)器對(duì)每一個(gè)group和user都有權(quán)限限制,group權(quán)限優(yōu)先于user權(quán)限,在
CheckFilePermissions 函數(shù)中可以看出。
conversion.* utf8和ansi字符的相互轉(zhuǎn)化
ExternalIpCheck.* PASV模式
根據(jù)配置獲取ip。
所有輔助文件已經(jīng)分析完畢,下級(jí)節(jié)開(kāi)始分析socket和線程類。