dcplusplus 是一个开源的 P2P 客户端,界面采用 WTL 编写,使用了 boost 库,是很好的范例教材。
dcplusplus 使用的第三方库有
- boost
- zlib
- minizip
- bzip2
- dht
- openssl
这个客户端的主要业务逻辑在 client
和 windows
目录
- 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 模式监听的作用就不大。