libtorrent应该是目前最完善的使用C++实现的bittorrent协议客户端开源开发库。有很多的开源bittorrent客户端都是基于这个开发库。
libtorrent本身使用了boost, sigc++这些库。 其中sigc++是libtorrent的基本框架, 所以移除sigc++的包依赖几乎不可能, 另外sigc++本身也依赖了某些库, 所以libtorrent向嵌入式方向的发展难度比较大。
libtorrent 采用的是轮询的方式实现的, 而不同于windows下开发人员动不动就多线程的方式不一样。 像rtorrent的话, 整个程序就只有一个线程, 所有的工作都在这个线程里面实现。 通过轮询得到网络事件,然后激发事件(事件的激发框架是由sigc++来实现), 在事件里面会继续处理数据的收发等。
轮询支持: epoll, kqueue, select三种方式。
对文件的读写的实现: 采用的是文件内存映射的方式实现, 所以看不到对本地磁盘的文件调用read/write 或者fread/fwrite操作。缓存的管理也是通过这种方式实现的。
由于事件/信号是程序的框架。 所以在这里大致的分析一下流程。
以官方网站上提供的一个简单的例子作为分析的参考:
wh
ile (!doShutdown) {
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&errorSet);
unsigned int maxFd = pollSelect->fdset(&readSet, &writeSet, &errorSet);
if (curlStack.is_busy())
maxFd = std::max(maxFd, curlStack.fdset(&readSet, &writeSet, &errorSet));
int64_t t = std::min<int64_t>(1000000, torrent::next_timeout());
timeval timeout = { t / 1000000, t % 1000000 };
if (select(maxFd + 1, &readSet, &writeSet, &errorSet, &timeout) == -1 &&
errno != EINTR)
throw std::runtime_error("Error polling.");
if (curlStack.is_busy())
curlStack.perform();
// 'torrent::perform()' updates the cached time and runs any
// scheduled tasks. We call it again to remove any task that
// might have immediate timeout or that have timed out during
// the call to 'pollSelect::perform(...)'.
torrent::perform();
pollSelect->perform(&readSet, &writeSet, &errorSet);
torrent::perform();
}
|
以上是程序的主循环了。
程序在进入循环前将建立一个CURL来读取种子的内容, 所以该种子可以来自网络的, 不一定是本地文件。
torrent::Http* curlGet = torrent::Http::call_factory();
curlGet->signal_done().connect(sigc::bind(sigc::ptr_fun(&http_done), curlGet));
进入主循环后, 当种子文件读取完毕。将激发信号
curlGet->signal_done().emit();
因为在之前已经将http_done与信号进行连接, 从而调用 http_done 函数。
在http_done里面将
分析种子文件, 并调用:
torrent::Download d = torrent::download_add(obj);
来为下载做准备。
与hash_check信号连接。
d.signal_hash_done(sigc::bind(sigc::ptr_fun(&hash_check_done), d));
开始hash检查 d.hash_check(false); /* 参数应该是false, 否则不支持继续下载(续传)。*/
文件hash检查完后将激发signal_hash_done信号。
所以程序会调用hash_check_done函数。
程序在hash_check_done里面启动下载(下栽前应该调用hash_check, 否则出错的)。
d.start();
现在就进入了 libtorrent 的地盘了。
在download::start里
首先 使tracker_manager进入调度队列。根据tracker的不同(包括 udp, dht, http的), 发送不同的请求。这里以 tracker_http为例。
使用curl来发送请求。 完成之后激发信号, 进入调用revceive_done()
分析返回的数据, 得到AddressList。 然后是 m_parent->receive_success(this, &l);
进入TrackerList::receive_success;
进入TrackerManager::receive_success
将tracker重新加入时间队列: priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_control->focus_normal_interval())).round_seconds());
然后是 m_slotSuccess(l);
进入 DownloadWrapper::receive_tracker_success
进入 DownloadMain::receive_connect_peers
m_slotStartHandshake(sa, this);
进入 HandshakeManager::add_outgoing
进入 create_outgoing
进入 Handshake::initialize_outgoing
连接完毕后自动进入 Handshake::event_write
握手完毕后: HandshakeManager::receive_succeeded
最后 pcb->event_read();
从而完成一个peer的连接。
阅读(5111) | 评论(0) | 转发(0) |