FileZilla Server原始碼分析
之所以有本系列的分析,是因為兩點:
本片作為開篇,略過如何編譯(該原始碼原始碼用VS2010編譯),如何配置,如何使用。FileZilla官網提供了程式和原始碼下載(原始碼包含在程式中,安裝時預設為不安裝),以及編譯步驟和注意事項,感興趣的朋友可以自行去官網尋找或google。
感謝:分析時參考了網友的系列文章《FileZilla FTP伺服器原始碼分析》,大家可以參照比對。
首先預覽一下原始碼目錄source資料夾下的大致檔案佈局。
6個子目錄,核心的程式碼(執行緒、socket、命令等)都放在當前目錄下。6個子目錄及對應程式碼功能:
當前source目錄下原始碼按實現功能大致又分為以下幾種型別:
檔案目錄結構分析完了,面對眾多.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。
這裡僅貼出兩處原始碼中呼叫這兩個訊息的例子,便可得知後面定義的幾個常數巨集的用處。
從上面程式碼可以看出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和執行緒類。
- FileZilla 是目前非常火爆的開源ftp專案,整個專案採用C++程式碼編寫,程式碼緊湊可讀性高,值得學習(缺陷是註釋太少)。
- 網路上已有的對該原始碼的分析基於的版本是0.9.18,分析比較粗略,無論是框架還是細節。
本片作為開篇,略過如何編譯(該原始碼原始碼用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;
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和執行緒類。
相關文章
- server_name 原始碼分析Server原始碼
- kestrel Server的原始碼分析Server原始碼
- Eureka 原始碼分析之 Eureka Server原始碼Server
- 以太坊原始碼分析(48)p2p-server.go原始碼分析原始碼ServerGo
- Swoole 原始碼分析——Server 模組之 OpenSSL (下)原始碼Server
- Swoole 原始碼分析——Server 模組之 OpenSSL (上)原始碼Server
- Swoole 原始碼分析——Server模組之OpenSSL (上)原始碼Server
- Kafka原始碼分析(三) - Server端 - 訊息儲存Kafka原始碼Server
- Kafka原始碼分析(四) - Server端-請求處理框架Kafka原始碼Server框架
- grpc python 原始碼分析(1):server 的建立和啟動RPCPython原始碼Server
- http server原始碼解析HTTPServer原始碼
- Swoole 原始碼分析——Server模組之ReactorThread事件迴圈(下)原始碼ServerReactthread事件
- [Web Server]Tomcat調優之SpringBoot內嵌Tomcat原始碼分析WebServerTomcatSpring Boot原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- gRPC-Server 啟動原始碼分析,一起弄懂它(八)RPCServer原始碼
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- goFrame 原始碼學習之 ServerGoFrame原始碼Server
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- k8s client-go原始碼分析 informer原始碼分析(6)-Indexer原始碼分析K8SclientGo原始碼ORMIndex
- k8s client-go原始碼分析 informer原始碼分析(4)-DeltaFIFO原始碼分析K8SclientGo原始碼ORM
- 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析Spring原始碼
- Spring原始碼分析——搭建spring原始碼Spring原始碼
- 以太坊原始碼分析(35)eth-fetcher原始碼分析原始碼
- 以太坊原始碼分析(20)core-bloombits原始碼分析原始碼OOM
- 以太坊原始碼分析(24)core-state原始碼分析原始碼
- 以太坊原始碼分析(29)core-vm原始碼分析原始碼
- 以太坊原始碼分析(34)eth-downloader原始碼分析原始碼