研究了一段时间的libtorrent,苦于对boost和asio的了解很有限,一直都没研究它的核发心部分……
为了增加士气,只好从外围好懂一点的模块着手研究……
torrent_info模块我觉得libtorrent做得很好。因为它可以方便地生成或者读取一个.torrent的信息,而不像bittorrent源码那些,读取.torrent是靠bencode/bdecode,生成.torrent却靠另外一个模块。在python读取bencode结构很方便,在C++中读取却很繁琐。torrent_info可以使libtorrent库用户无需了解太多相关的知识而可以实现BT协议,不知不说它真的做得比较好。还有将它们合并在一起,增加了模块的内聚性。另外它还提供了piece和文件位置的映射功能。
torrent_ifno接借了如下接口
要说明一下的是entry,一个entry代表的是一个bencode的一个结点。
classtorrent_info
{
public:
torrent_info();
torrent_info(sha1_hashconst&info_hash);
torrent_info(entryconst&torrent_file);//从一个文件取读数据后,经过bdecode函数转换成entry,之再转输入到这里
entry create_torrent()const;//根据本对象现存的信息,每成一个entry对象
voidset_comment(charconst* str);//设置注释
voidset_piece_size(intsize);//设置piece大小
voidset_creator(charconst* str);//设置创建程序名称
voidset_hash(intindex, sha1_hash const& h);//设置某一piece的SHA-1 digest
voidadd_tracker(std::stringconst& url,int tier = 0);//添加tracker服务器,tier代表优先级,0最高
voidadd_file(boost::filesystem::path file,size_type size);//添加一个文件信息
voidadd_url_seed(std::stringconst& url);//添加一个url seed地址
typedefstd::vector<file_entry>::const_iterator file_iterator;
typedefstd::vector<file_entry>::const_reverse_iterator
reverse_file_iterator;
file_iterator begin_files()const;//返回文件信息的迭代器
file_iterator end_files()const;
reverse_file_iteratorrbegin_files()const;//返回文件信息的逆向迭代器
reverse_file_iterator rend_files()const;
int num_files()const;//返回文件数量
file_entry const& file_at(int index)const;//根据文件信息录入的序号返回文件信息
std::vector<file_slice>map_block(intpiece,size_type offset
,intsize)const;//??
peer_request map_file(int file_index,size_type file_offset
,intsize)const;//??
std::vector<announce_entry>const& trackers()const;//返回tracker列表
std::vector<std::string>const& url_seeds()const;//返回url seed 列表
size_typetotal_size()const;//返回所有文件大小的总和
size_typepiece_length()const;//返回piece长度
intnum_pieces()const;//返回piece数量
sha1_hash const& info_hash()const;//返回.torrent的info_hash
std::stringconst& name()const;//返回.torrent的name
std::stringconst&comment()const;//返回注释
std::stringconst&creator()const;//返回创建程序名称
boost::optional<boost::posix_time::ptime>
creation_date()const;//返回创建时间
voidprint(std::ostream& os)const;//输出所有信息到标准输出流
size_typepiece_size(unsignedint index)const;//返回指定piece的大小,只有最后一个piece的大小不同于其它piece
sha1_hash const& hash_for_piece(unsignedint index)const;//返回指定piece的SHA-1hash
};
要用代码说明torrent_info如何使用,最好的代码莫过于libtorrent的例子make_torrent.cpp
#include<iostream>
#include<fstream>
#include<iterator>
#include<iomanip>
#include"libtorrent/entry.hpp"
#include"libtorrent/bencode.hpp"
#include"libtorrent/torrent_info.hpp"
#include"libtorrent/file.hpp"
#include"libtorrent/storage.hpp"
#include"libtorrent/hasher.hpp"
#include"libtorrent/file_pool.hpp"
#include<boost/filesystem/operations.hpp>
#include<boost/filesystem/path.hpp>
#include<boost/filesystem/fstream.hpp>
usingnamespace boost::filesystem;
usingnamespace libtorrent;
void add_files(
torrent_info& t
, path const& p
, path const& l)
{
if(l.leaf()[0]=='.')return;
path f(p /l);
if(is_directory(f))
{
for(directory_iteratori(f), end; i != end;++i)
add_files(t,p, l / i->leaf());
}
else
{
std::cerr<<"adding\""<< l.string()<<"\"\n";
t.add_file(l,file_size(f));
}
}
int main(intargc,char*argv[])
{
usingnamespace libtorrent;
usingnamespace boost::filesystem;
path::default_name_check(no_check);
if(argc!= 4 && argc != 5)
{
return 1;
}
try
{
torrent_info t;
path full_path =complete(path(argv[3]));
ofstream out(complete(path(argv[1])),std::ios_base::binary);
intpiece_size = 256 * 1024;
charconst* creator_str ="libtorrent";
add_files(t,full_path.branch_path(),full_path.leaf());
t.set_piece_size(piece_size);
file_pool fp;
storage st(t,full_path.branch_path(), fp);
t.add_tracker(argv[2]);
// calculate the hash for all pieces
int num =t.num_pieces();
std::vector<char> buf(piece_size);
for(int i = 0; i < num;++i)
{
st.read(&buf[0], i, 0, t.piece_size(i));
hasher h(&buf[0], t.piece_size(i));
t.set_hash(i,h.final());
std::cerr<<(i+1)<<"/"<< num <<"\r";
}
t.set_creator(creator_str);
if(argc== 5)
t.add_url_seed(argv[4]);
// create the torrent and print it to out
entry e = t.create_torrent();
libtorrent::bencode(std::ostream_iterator<char>(out), e);
}
catch(std::exception& e)
{
std::cerr<< e.what()<<"\n";
}
return 0;
}