Chinaunix首页 | 论坛 | 博客
  • 博客访问: 905745
  • 博文数量: 201
  • 博客积分: 8078
  • 博客等级: 中将
  • 技术积分: 2162
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-20 17:22
文章分类

全部博文(201)

文章存档

2013年(3)

2012年(11)

2011年(34)

2010年(25)

2009年(51)

2008年(77)

分类: 系统运维

2008-05-27 14:50:20

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的连接。



阅读(4888) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~