P2P的核心特点决定了P2P必须从多个peer下载,这样必然要求将下载的内容分段-chunk,在这些chunk中先下哪一个呢?或者说哪一个chunk最宝贵呢,市场经济早就告诉了我们,物以稀为贵。这也是BT chunk选择算法最重要最基本的,当然在不同的应用中还会加上一些权重(比如视音频的索引chunk,谁让人家是衔玉而生呢),为了确定稀缺度,我们需要进行精确的汇总统计,torrent::ChunkStatistics 类就完成这个工作.
原型: class ChunkStatistics: public std::vector<uint8_t>
说明: vector派生类,下标为chunk index, 值为“市场上”货物数目,如[1]=10 表示index为1的chunk有10个peer拥有。既然是统计chunk数的,其必然是归download,所有peer共享一个,在实际中是DownloadMain的成员变量m_chunkStatistics
调用时机:在BT协议中,当peer握手成功后,交换bitfield会调用received_connect;在收到HAV Message时会调用received_have_chunk;在Peer断开时会调用received_disconnect。
注意:因为是 uint8_t的vector,这表示最大的统计数是 255个,由函数 should_add(PeerChunks* pc) 进行检查
成员变量:
size_type m_complete; //拥有所有chunk的peer数,即seeder数size_type m_accounted; //拥有部分chunk的peer数,即leecher数
核心函数:
//初始化vector 大小,参数:chunk 数目 ,由 DownloadMain::open调用
void ChunkStatistics::initialize(size_type s) {
if(!empty())
throwinternal_error("ChunkStatistics::initialize(...) called on an initialized object.");
base_type::resize(s);
}
//PeerConnectionBase::initialize 函数中调用,pc->using_counter记录是否被统计过 void ChunkStatistics::received_connect(PeerChunks* pc) { if (pc->using_counter()) throw internal_error("ChunkStatistics::received_connect(...) pc->using_counter() == true."); //peer是seeder if (pc->bitfield()->is_all_set()) { pc->set_using_counter(true); m_complete++; } else if (!pc->bitfield()->is_all_unset() && should_add(pc)) { //peer是leech pc->set_using_counter(true); m_accounted++; iterator itr = base_type::begin(); //加 for (Bitfield::size_type index = 0; index < pc->bitfield()->size_bits(); ++index, ++itr) *itr += pc->bitfield()->get(index); } }
//PeerConnectionBase::cleanup调用 void ChunkStatistics::received_disconnect(PeerChunks* pc) { //没有统计过. if (!pc->using_counter()) return; pc->set_using_counter(false); if (pc->bitfield()->is_all_set()) { m_complete--; } else { if (m_accounted == 0) throw internal_error("ChunkStatistics::received_disconnect(...) m_accounted == 0."); m_accounted--; iterator itr = base_type::begin(); //减. for (Bitfield::size_type index = 0; index < pc->bitfield()->size_bits(); ++index, ++itr) *itr -= pc->bitfield()->get(index); } } //PeerConnection ::read_have_chunk(uint32_t index) 调用 void ChunkStatistics::received_have_chunk(PeerChunks* pc, uint32_t index, uint32_t length) { // When the bitfield is empty, it is very cheap to add the peer to // the statistics. It needs to be done here else we need to check if // a connection has sent any messages, else it might send a bitfield. if (pc->bitfield()->is_all_unset() && should_add(pc)) { if (pc->using_counter()) throw internal_error("ChunkStatistics::received_have_chunk(...) pc->using_counter() == true."); pc->set_using_counter(true); m_accounted++; } pc->bitfield()->set(index); pc->peer_rate()->insert(length); if (pc->using_counter()) { base_type::operator[](index)++; //peer由leech 转变为 seeder if (pc->bitfield()->is_all_set()) { if (m_accounted == 0) throw internal_error("ChunkStatistics::received_disconnect(...) m_accounted == 0."); m_complete++; m_accounted--; for (iterator itr = base_type::begin(), last = base_type::end(); itr != last; ++itr) *itr -= 1; } } else { if (pc->bitfield()->is_all_set()) { pc->set_using_counter(true); m_complete++; } } }
//获得index的统计数 const_reference rarity(size_type n) const { return base_type::operator[](n); } const_reference operator [] (size_type n) const { return base_type::operator[](n); }