dcplusplus 源码分析

dcplusplus 是一个开源的 P2P 客户端,界面采用 WTL 编写,使用了 boost 库,是很好的范例教材。

dcplusplus 使用的第三方库有

  • boost
  • zlib
  • minizip
  • bzip2
  • dht
  • openssl

这个客户端的主要业务逻辑在 clientwindows 目录

  • client 存放业务逻辑
  • windows 基于 WTL 的各种 UI

使用的模式

  • 单件模式
  • MVC 模式

此客户端采用单件模式分割各个模块,采用 MVC 更新视图。

client\Singleton.h

#ifndef DCPLUSPLUS_DCPP_SINGLETON_H
#define DCPLUSPLUS_DCPP_SINGLETON_H

#include "debug.h"

namespace dcpp {

template<typename T>
class Singleton {
public:
    Singleton() { }
    virtual ~Singleton() { }

    static T* getInstance() {
        dcassert(instance);
        return instance;
    }

    static void newInstance() {
        if(instance)
            delete instance;

        instance = new T();
    }

    static void deleteInstance() {
        if(instance)
            delete instance;
        instance = NULL;
    }
protected:
    static T* instance;
private:
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

};

template<class T> T* Singleton<T>::instance = NULL;

} // namespace dcpp

#endif // DCPLUSPLUS_DCPP_SINGLETON_H

此单件模板类并不是线程安全,实际上如果做模块划分,系统直接在主线程中初始化各个模块。

client\Speaker.h

#ifndef DCPLUSPLUS_DCPP_SPEAKER_H
#define DCPLUSPLUS_DCPP_SPEAKER_H

#include <boost/range/algorithm/find.hpp>
#include <utility>
#include <vector>

#include "Thread.h"

#include "noexcept.h"

namespace dcpp {

using std::forward;
using std::vector;
using boost::range::find;

template<typename Listener>
class Speaker {
    typedef vector<Listener*> ListenerList;

public:
    Speaker() noexcept { }
    virtual ~Speaker() { }

    /// @todo simplify when we have variadic templates

    template<typename T0>
    void fire(T0&& type) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type));
        }
    }

    template<typename T0, typename T1>
    void fire(T0&& type, T1&& p1) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1));
        }
    }

    template<typename T0, typename T1, typename T2>
    void fire(T0&& type, T1&& p1, T2&& p2) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2));
        }
    }

    template<typename T0, typename T1, typename T2, typename T3>
    void fire(T0&& type, T1&& p1, T2&& p2, T3&& p3) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2), forward<T3>(p3));
        }
    }

    template<typename T0, typename T1, typename T2, typename T3, typename T4>
    void fire(T0&& type, T1&& p1, T2&& p2, T3&& p3, T4&& p4) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2), forward<T3>(p3), forward<T4>(p4));
        }
    }

    template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
    void fire(T0&& type, T1&& p1, T2&& p2, T3&& p3, T4&& p4, T5&& p5) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2), forward<T3>(p3), forward<T4>(p4), forward<T5>(p5));
        }
    }

    template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
    void fire(T0&& type, T1&& p1, T2&& p2, T3&& p3, T4&& p4, T5&& p5, T6&& p6) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2), forward<T3>(p3), forward<T4>(p4), forward<T5>(p5), forward<T6>(p6));
        }
    }

    template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
    void fire(T0&& type, T1&& p1, T2&& p2, T3&& p3, T4&& p4, T5&& p5, T6&& p6, T7&& p7) noexcept {
        Lock l(listenerCS);
        tmp = listeners;
        for(auto i = tmp.begin(); i != tmp.end(); ++i) {
            (*i)->on(forward<T0>(type), forward<T1>(p1), forward<T2>(p2), forward<T3>(p3), forward<T4>(p4), forward<T5>(p5), forward<T6>(p6), forward<T7>(p7));
        }
    }

    void addListener(Listener* aListener) {
        Lock l(listenerCS);
        if(find(listeners, aListener) == listeners.end())
            listeners.push_back(aListener);
    }

    void removeListener(Listener* aListener) {
        Lock l(listenerCS);
        auto it = find(listeners, aListener);
        if(it != listeners.end())
            listeners.erase(it);
    }

    void removeListeners() {
        Lock l(listenerCS);
        listeners.clear();
    }
    
protected:
    ListenerList listeners;
    ListenerList tmp;
    CriticalSection listenerCS;
};

} // namespace dcpp

#endif // !defined(SPEAKER_H)

更新视图

dcplusplus 并不直接访问业务类返回数据,更新UI。通常这样的做法是访问业务类封装在线程里,异步返回数据,更新UI。 但是 dcplusplus 采用业务类访问数据后直接放置在队列容器里,PostMessage(WM_SPEAKER);onSpeaker(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 消息响应中访问数据更新视图。

client\TaskQueue.h

#ifndef DCPLUSPLUS_DCPP_TASK_H
#define DCPLUSPLUS_DCPP_TASK_H

#include <memory>
#include <utility>
#include <vector>

#include "Thread.h"

namespace dcpp {

using std::make_pair;
using std::pair;
using std::swap;
using std::unique_ptr;
using std::vector;

struct Task {
    virtual ~Task() { };
};

struct StringTask : public Task {
    StringTask(const string& str_) : str(str_) { }
    string str;
};

class TaskQueue {
public:
    typedef pair<uint8_t, unique_ptr<Task> > Pair;
    typedef vector<Pair> List;

    TaskQueue() {
    }

    ~TaskQueue() {
        clear();
    }

    void add(uint8_t type, std::unique_ptr<Task> && data) { Lock l(cs); tasks.push_back(make_pair(type, move(data))); }
    void get(List& list) { Lock l(cs); swap(tasks, list); }
    void clear() {
        List tmp;
        get(tmp);
    }
private:

    TaskQueue(const TaskQueue&);
    TaskQueue& operator=(const TaskQueue&);

    CriticalSection cs;
    List tasks;
};

} // namespace dcpp

#endif

建立业务类的关系

注册监听

FinishedManager::FinishedManager() { 
    QueueManager::getInstance()->addListener(this);
    UploadManager::getInstance()->addListener(this);
    SettingsManager::getInstance()->addListener(this);
}
    
FinishedManager::~FinishedManager() {
    QueueManager::getInstance()->removeListener(this);
    UploadManager::getInstance()->removeListener(this);
    SettingsManager::getInstance()->removeListener(this);

    Lock l(cs);
    for_each(downloads.begin(), downloads.end(), DeleteFunction());
    for_each(uploads.begin(), uploads.end(), DeleteFunction());
}

由于各个模块都是单件,可以直接访问并注册自己this

各个模块的初始化

client\DCPlusPlus.cpp

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "ConnectionManager.h"
#include "DownloadManager.h"
#include "UploadManager.h"
#include "CryptoManager.h"
#include "ShareManager.h"
#include "SearchManager.h"
#include "QueueManager.h"
#include "ClientManager.h"
#include "HashManager.h"
#include "LogManager.h"
#include "FavoriteManager.h"
#include "SettingsManager.h"
#include "FinishedManager.h"
#include "ADLSearch.h"
#include "MappingManager.h"
#include "ConnectivityManager.h"

#include "DebugManager.h"
#include "DetectionManager.h"
#include "WebServerManager.h"
#include "ThrottleManager.h"
#include "File.h"

#include "RawManager.h"
#include "PluginManager.h"
#include "HttpManager.h"
#include "UpdateManager.h"

#include "../dht/DHT.h"
#ifdef _WIN32
# include "../windows/PluginApiWin.h"
#endif

/*
#ifdef _STLP_DEBUG
void __stl_debug_terminate() {
    int* x = 0;
    *x = 0;
}
#endif
*/

namespace dcpp {

using dht::DHT;

void startup(void (*f)(void*, const string&), void* p) {
    // "Dedicated to the near-memory of Nev. Let's start remembering people while they're still alive."
    // Nev's great contribution to dc++
    while(1) break;


#ifdef _WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

    Util::initialize();

    ResourceManager::newInstance();
    SettingsManager::newInstance();

    TimerManager::newInstance();
    LogManager::newInstance();
    HashManager::newInstance();
    CryptoManager::newInstance();
    SearchManager::newInstance();
    ClientManager::newInstance();
    ConnectionManager::newInstance();
    DownloadManager::newInstance();
    UploadManager::newInstance();
    ThrottleManager::newInstance();
    QueueManager::newInstance();
    ShareManager::newInstance();
    FavoriteManager::newInstance();
    FinishedManager::newInstance();
    RawManager::newInstance();
    ADLSearchManager::newInstance();
    ConnectivityManager::newInstance();
    MappingManager::newInstance();
    DebugManager::newInstance();
    WebServerManager::newInstance();
    DetectionManager::newInstance();
    PluginManager::newInstance();
    PluginApiImpl::init();
#ifdef _WIN32
    PluginApiWin::init();
#endif
    HttpManager::newInstance();
    UpdateManager::newInstance();

    SettingsManager::getInstance()->load(); 

    if(!SETTING(LANGUAGE_FILE).empty()) {
        string languageFile = SETTING(LANGUAGE_FILE);
        if(!File::isAbsolute(languageFile))
            languageFile = Util::getPath(Util::PATH_LOCALE) + languageFile;
        ResourceManager::getInstance()->loadLanguage(languageFile);
    }

    FavoriteManager::getInstance()->load();

    CryptoManager::getInstance()->loadCertificates();
    DetectionManager::getInstance()->load();
    PluginManager::getInstance()->loadPlugins([&f, p](const string& str) { (*f)(p, str); });
    
    DHT::newInstance();

    if(f != NULL)
        (*f)(p, STRING(HASH_DATABASE));
    HashManager::getInstance()->startup();
    if(f != NULL)
        (*f)(p, STRING(SHARED_FILES));
    ShareManager::getInstance()->refresh(true, false, true);
    if(f != NULL)
        (*f)(p, STRING(DOWNLOAD_QUEUE));
    QueueManager::getInstance()->loadQueue();

    UpdateManager::cleanTempFiles();
}

void shutdown() {
    PluginManager::getInstance()->unloadPlugins();
    TimerManager::getInstance()->shutdown();
    HashManager::getInstance()->shutdown();
    ConnectionManager::getInstance()->shutdown();
    WebServerManager::getInstance()->shutdown();
    MappingManager::getInstance()->close();
    BufferedSocket::waitShutdown();
    
    QueueManager::getInstance()->saveQueue(true);
    SettingsManager::getInstance()->save();

    UpdateManager::deleteInstance();
    HttpManager::deleteInstance();
    PluginApiImpl::shutdown();
    PluginManager::deleteInstance();
    DetectionManager::deleteInstance();
    WebServerManager::deleteInstance();
    DebugManager::deleteInstance();
    MappingManager::deleteInstance();
    ConnectivityManager::deleteInstance();
    DHT::deleteInstance();
    ADLSearchManager::deleteInstance();
    RawManager::deleteInstance();
    FinishedManager::deleteInstance();
    ShareManager::deleteInstance();
    CryptoManager::deleteInstance();
    ThrottleManager::deleteInstance();
    DownloadManager::deleteInstance();
    UploadManager::deleteInstance();
    QueueManager::deleteInstance();
    ConnectionManager::deleteInstance();
    SearchManager::deleteInstance();
    FavoriteManager::deleteInstance();
    ClientManager::deleteInstance();
    HashManager::deleteInstance();
    LogManager::deleteInstance();
    TimerManager::deleteInstance();
    SettingsManager::deleteInstance();
    ResourceManager::deleteInstance();

#ifdef _WIN32
    ::WSACleanup();
#endif
}

} // namespace dcpp

单件类的生命周期很长,一般与应用程序生命周期一样长,适合做模块划分,MVC 模式作用在单件类上,有时间更新监听者,如果是生命周期很短的小类,那么 MVC 模式监听的作用就不大。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容