项目依赖库:

  1. bison
  2. gmp
  3. gnutls
  4. help2man
  5. nettle
  6. sqlite
  7. texinfo
  8. zlib
  9. wxWidgets: https://www.wxwidgets.org/
  10. libfilezilla

编译顺序

  1. 先编译->libfilezilla/libfilezilla.sln
  2. 再编译->filezilla/src/FileZilla.sln

启动流程

  1. 先调用CFileZilla的构造函数
  2. 再调用CFileZilaApp::OnInit 方法
  3. 程序退出时,调用CFileZilaApp::OnExit方法

通过SSH协议连接Linux机器流程

Filezilla.exe 编译时依赖

  1. 主程序 FileZilla,exe 可执行程序
  2. fzshellext,FileZilla用于扩展Windows Shell右键菜单的模块,dll模块,动态加载
  3. engine,FileZilla的核心逻辑
  4. commonui,公共的ui逻辑,静态库
  5. 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
2
3
HINSTANCE hDll = LoadLibrary("xxx.dll");
PfuncA* pFuncA = GetProcAddress(hDll, "funcA");
pFuncA();
3. 静态链接
    让一个VS的项目生成一个静态库,生成XXX.lib文件,此时xxx.lib包含所有的实现。
    exe来连接这个xxx.lib时,把xxx.lib中所有的实现。exe来连接这个xxx.lib,把xxx.lib中所有的实现全部包含到自己的程序文件中。

Windows 平台用程序启动一个新的进程的方法

  1. CreateProcess
  2. 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

工程实践规则

  1. 编写C++类时,如果明确不需要构造、析构、拷贝、operatr=拷贝函数,你应该使用=delete语法禁止编译器生成。

    1
    2
    3
    4
    FileZillaEngine.h
    CFileZillaEngine(CFileZillaEngine const&) = delete;
    CFileZillaEngine& operator=(CFileZillaEngine const&) = delete;

  2. C++17 的 std::string_view

    1
    2
    3
    string.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。

  1. 要善用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
    55
    class 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);
    }
  2. scoped_mutex锁 作用域锁
    RAII惯用法

  3. mutable 关键字的使用

    1
    2
    3
    engineprivate.h
    // General mutex for operations on this engines
    mutable fz::mutex mutex_;
  4. 智能指针是std::shared_ptr、std::unique_ptr 重载了operator->、operator!等运算符。

7.Commend设计模式

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
class FZC_PUBLIC_SYMBOL CCommand
{
public:
CComand() = default;

virtual Command GetId() const = 0;
virtual CCommand *Clone() const = 0;
virtual bool valid() const { return true; }
protected:
CCommand(CComand const&) = default;
CCommand& operator=(CCommand const&) = default;
}

template<typename Derived, Command id>
class FZC_PUBLIC_SYMBOL CCommandHelper : public CCommand
{
public:
virtual Command GetId() const final { return id; }
virtual CCommand* Clone() const final { return new Derived(static_cast<Derived const&>(*this)); }
protected:
CCommandHelper<Derived, id>() = default;
CCommandHelper<Derived, id>(CCommandHelper<Derived, id> const&) = default;
CCommandHelper<Derived, id>& operator=(CCommandHelper<Derived, id> const&) = default
}

CConnectCommand -> CCommandHelper<CConnectCommand, Command::connect>
CListCommend -> CCommandHelper<CListCommand, Command::list>

void CCommandQueue::ProcessCommand()
void CCommandQueue::ProcessNextCommand()
  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
    27
    template<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
2
3
event_loop.hpp
typedef std::deque<std::tuple<event_handler*, event_base*, bool>> Events;
Events pending_events_;

⬇️

emplace_back 原位构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 利用handler, evt, deletable参数构造一个tuple,放入pending_events_
pending_events_.emplace_back(handler, evt, deletable);

// 构造myTuple
// 利用myTuple拷贝构造一个新的tuple,放入pending_events_
// 析构myTuple
std::tuple<event_handler*, event_base*, bool> myTuple(handler, evt, deletable);
pending_events_.push_back(myTuple);

// ----------------
// 以下emplace_back和push_back效率上一样
std::vector<int> myV;
myV.emplace_back(1);
myV.push_back(1);

将函数指针和函数需要的参数绑定在一个元组

1
2
// 函数指针是类重载的(),是仿函数
(*std::get<0>(ev))(*std::get<1>(ev));

可调用的函数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
operator()() {
// 执行的一些逻辑
}

operator()(const char* pz) {
// 执行的一些逻辑
}
}

A a;
a();
a("123");

不定参数模板的函数调用

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
template <typename T, typename... args>
void send() {

}

template<typename T>
void send() {

}
class A {

}
class B {

}

send<A>();
send<A, B>();
send<A, B, C>();

this->operator(event_base)

void CFileZillaEnginePrivate::operator()(fz::event_base const& ev)
{
.....
}

函数调用约定

所谓函数调用约定指的是函数的参数入栈顺序以及函数调用结束之后,由谁来清理栈空间。

1
2
3
void f1(int a, int b, int c) {
f2();
}
  1. _cdel,调用者清理堆栈,f1清理栈

  2. __stdcall,被调用者清理堆栈,f2清理栈

  3. _thiscall,落实到1、2

模块拆分细致

协议模块 事件模块 连接模块

Filezilla连接Linux主机的流程

点击Site Manager对话框的Connect按钮

⬇️

CSiteManagerDialog::OnConnect

⬇️

CMainFrame::ConnectToSite