源码阅读 - filezilla(1)
项目依赖库:
- bison
- gmp
- gnutls
- help2man
- nettle
- sqlite
- texinfo
- zlib
- wxWidgets: https://www.wxwidgets.org/
- libfilezilla
编译顺序
- 先编译->libfilezilla/libfilezilla.sln
- 再编译->filezilla/src/FileZilla.sln
启动流程
- 先调用CFileZilla的构造函数
- 再调用CFileZilaApp::OnInit 方法
- 程序退出时,调用CFileZilaApp::OnExit方法
通过SSH协议连接Linux机器流程
Filezilla.exe 编译时依赖
- 主程序 FileZilla,exe 可执行程序
- fzshellext,FileZilla用于扩展Windows Shell右键菜单的模块,dll模块,动态加载
- engine,FileZilla的核心逻辑
- commonui,公共的ui逻辑,静态库
- pugixml,轻量级的xml解析库
依赖库的加载
Windows平台(PE Portable Execute)
可执行程序:.exe .dll .ocx .sys
直接可以运行的程序:.exe
一个exe程序可能会依赖一些库文件(dll: dynamic Link Library)
一个exe程序如何加载dll文件:
1. 隐式加载
在VS中exe程序所在的工程,依赖一个XXX.lib(二进制角度),同时包含xxx.h(源码角度)等相关头文件,实际运行时,需要提供一个xxx.dll文件。生成了exe之后,exe的导入表中就有 XXX.dll的信息。
2. 显示加载
1 | |
3. 静态链接
让一个VS的项目生成一个静态库,生成XXX.lib文件,此时xxx.lib包含所有的实现。
exe来连接这个xxx.lib时,把xxx.lib中所有的实现。exe来连接这个xxx.lib,把xxx.lib中所有的实现全部包含到自己的程序文件中。
Windows 平台用程序启动一个新的进程的方法
- CreateProcess
- ShellExecute()
Linux平台(ELF文件)
可执行程序,文件属性来的
-rwx r-x r-x 1 root root 882296 Mar 10 22:14 calc24Server
chmod + x calc24Server
r = 4
w = 2
x = 1
Linux的依赖库
*.so(shared object)
*.a
工程实践规则
编写C++类时,如果明确不需要构造、析构、拷贝、operatr=拷贝函数,你应该使用=delete语法禁止编译器生成。
1
2
3
4FileZillaEngine.h
CFileZillaEngine(CFileZillaEngine const&) = delete;
CFileZillaEngine& operator=(CFileZillaEngine const&) = delete;C++17 的 std::string_view
1
2
3string.hpp
int FZ_PUBLIC_SYMBOL stricmp(std::string_view const& a, std::string_view const& b);
int FZ_PUBLIC_SYMBOL stricmp(std::wstring_view const& a, std::wstring_view const& b);
对某个std::string的快照,不能修改std::string, string_view相比std::string更轻量。能使用std::string_view的地方尽量使用std::string_view。
要善用pimpl惯用法,隐藏不必要暴露的内部实现
(Pointer to implementation)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55class FileZillaEngine
{
// ....
std::unique_ptr<CFileZillaEnginePrivate> impl_;
}
// ----------------------
// demo示例
// Network.h
#include <cstdint>
#include <memory>
class Network {
public:
explicit Network();
~Network();
Network(const Network& rhs) = delete;
NetWork& operator=(const Network& rhs) = delete;
public:
void connect(const char* ip, uint16_t port);
private:
class Impl;
std::unique_ptr<Impl> m_pImpl;
}
// Network.cpp
#include "Network.h"
#include <string>
class Network::Impl {
public:
void connect(const char* ip, uint16_t port) {
//TODO: 利用成员变量编写实现细节
//m_a
}
private:
int m_a;
std::string m_b;
}
Network::Network() {
m_pImpl = std::make_unique<Impl>>();
}
Network::~Network() {
}
void connect(const char* ip, uint16_t port) {
m_pImpl->connect(ip, port);
}scoped_mutex锁 作用域锁
RAII惯用法mutable 关键字的使用
1
2
3engineprivate.h
// General mutex for operations on this engines
mutable fz::mutex mutex_;智能指针是std::shared_ptr、std::unique_ptr 重载了operator->、operator!等运算符。
7.Commend设计模式
1 | |
- CTRP(奇异模板递归模式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27template<typename Derived>
class Parent {
// 在父类中做子类才能做的事情
virtual void f1() {
// 引入了虚表,在运行时调用子类的方法
}
void f2() {
// 不用引入虚表也可以实现运行时在,通过父类型调用子类方法
myson.f2();
}
private:
Derived myson;
}
class Son : public Parent<Son> {
public:
virtual void f1() override {
// 引入虚表
}
void f2() {
}
}
deque C++17 双端队列
1 | |
⬇️
emplace_back 原位构造
1 | |
将函数指针和函数需要的参数绑定在一个元组
1 | |
可调用的函数对象
1 | |
不定参数模板的函数调用
1 | |
函数调用约定
所谓函数调用约定指的是函数的参数入栈顺序以及函数调用结束之后,由谁来清理栈空间。
1 | |
_cdel,调用者清理堆栈,f1清理栈
__stdcall,被调用者清理堆栈,f2清理栈
_thiscall,落实到1、2
模块拆分细致
协议模块 事件模块 连接模块
Filezilla连接Linux主机的流程
点击Site Manager对话框的Connect按钮
⬇️
CSiteManagerDialog::OnConnect
⬇️
CMainFrame::ConnectToSite


