最近在做的一个P2P项目需要使用到一些基础的IO,网络通信的模块,自己写的话短时间内很难达到产品级的健壮性,所以就从现有的开源项目里面抽取模块了,看了看eMule的,这东西和MFC结合紧密,而且整个项目大的可怕,要编译成功都很麻烦,更不要说去抽取它的模块了。不大靠谱。于是就研究了一下LibTorrent,不过这东西有两个。我研究的是Rasterbar的版本。现在的版本是0.15.0。不过这东西整了个openssl的功能。openssl在windows下编译麻烦死了,关键还得装个perl还是python忘了,代码也复杂了。所以我花了点时间把openssl给剥离出去。还好作者写的代码思路清晰,都用宏定义划分了不同功能的分支。
libtorrent的类结构中最大的一个结构就是alert结构了(点击看大图).
alert
对于外部程序来说,libtorrent的pop_alert()函数正是外部程序用来从libtorrent中获取各种消息、警告、错误等信息的接口。按照官方文档的说明,如果没有任何可用的这些信息的话,就自动返回一个默认构造的auto_ptr对象。由此可见。这个alert的继承树中的各种具体的对象,就分别代表了不同的信息了。对于libtorrent来说,它内部维护了一个alert的队列。此外,libtorrent默认是只保存错误消息的。可以使用set_alert_mask来设置。当然,获得了一个auto_ptr对象的话自然还必须知道具体是哪一个,所以libtorrent就使用alert_cast<>来获得具体的详细类型。alert的具体类型都定义在libtorrent/alert_types.hpp文件中。
alert的代码是这样的:
class TORRENT_EXPORT alert
{
public:
// only here for backwards compatibility
enum severity_t { debug, info, warning, critical, fatal, none };
enum category_t
{
error_notification = 0x1,
peer_notification = 0x2,
port_mapping_notification = 0x4,
storage_notification = 0x8,
tracker_notification = 0x10,
debug_notification = 0x20,
status_notification = 0x40,
progress_notification = 0x80,
ip_block_notification = 0x100,
performance_warning = 0x200,
dht_notification = 0x400,
stats_notification = 0x800,
all_categories = 0xffffffff
};
alert();
virtual ~alert();
// a timestamp is automatically created in the constructor
ptime timestamp() const;
virtual char const* what() const = 0;
virtual std::string message() const = 0;
virtual int category() const = 0;
#ifndef TORRENT_NO_DEPRECATE
TORRENT_DEPRECATED_PREFIX
severity_t severity() const TORRENT_DEPRECATED { return warning; }
#endif
virtual std::auto_ptr<alert> clone() const = 0;
private:
ptime m_timestamp;
};
关于每个alert的具体描述,官方文档这里有说明。
libtorrent_exception
libtorrent中的不少函数有两个版本,一个会抛出异常,一个是接受一个引用,返回异常代码。因为直接使用了boost的error code来表示错误,所以结构比较简单。
storage_interface
storage_interface是libtorrent中我最关心的一部分了,这个接口是个纯虚函数,目的是可以让开发者定制特定的种子对应的文件保存位置,默认使用一个代表存储在本地磁盘的文件的实现。storage_interface是基于槽实现的,每个槽有piece_size个字节。所有对磁盘的读写都是基于完整的或者部分的槽。
struct TORRENT_EXPORT storage_interface
{
storage_interface(): m_disk_pool(0), m_settings(0) {}
// create directories and set file sizes
// if allocate_files is true.
// allocate_files is true if allocation mode
// is set to full and sparse files are supported
// false return value indicates an error
virtual bool initialize(bool allocate_files) = 0;
// 此函数在第一次检查(重检查)种子所对应的文件时调用,如果文件已存在应该返回true。
// 并且,这种情况下这个文件已有的片段应该在下载开始前被检查
virtual bool has_any_file() = 0;
// 以下两个函数用于从给定的offset处的给定的slot中读写数据。此方法将连续读写
// num_bufs个buffer的内容,其中每个buffer的大小都将在bufs数组中定义。
// file::iovec_t类型是这样定义的:
// struct iovec_t
// {
// void* iov_base;
// size_t iov_len;
// };
// 返回值则是实际读写的字节数。或者返回-1表示失败。
// 每个bufs中的buffer都能被假定为是按页及页大小对准的,当然,除了种子中的最后一个buffer。
virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs);
virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs);
// negative return value indicates an error
virtual int read(char* buf, int slot, int offset, int size) = 0;
// negative return value indicates an error
virtual int write(const char* buf, int slot, int offset, int size) = 0;
virtual size_type physical_offset(int slot, int offset) = 0;
// returns the end of the sparse region the slot 'start'
// resides in i.e. the next slot with content. If start
// is not in a sparse region, start itself is returned
virtual int sparse_end(int start) const { return start; }
// non-zero return value indicates an error
virtual bool move_storage(fs::path save_path) = 0;
// verify storage dependent fast resume entries
virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0;
// write storage dependent fast resume entries
virtual bool write_resume_data(entry& rd) const = 0;
// moves (or copies) the content in src_slot to dst_slot
virtual bool move_slot(int src_slot, int dst_slot) = 0;
// swaps the data in slot1 and slot2
virtual bool swap_slots(int slot1, int slot2) = 0;
// swaps the puts the data in slot1 in slot2, the data in slot2
// in slot3 and the data in slot3 in slot1
virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0;
// this will close all open files that are opened for
// writing. This is called when a torrent has finished
// downloading.
// non-zero return value indicates an error
virtual bool release_files() = 0;
// this will rename the file specified by index.
virtual bool rename_file(int index, std::string const& new_filename) = 0;
// this will close all open files and delete them
// non-zero return value indicates an error
virtual bool delete_files() = 0;
disk_buffer_pool* disk_pool() { return m_disk_pool; }
session_settings const& settings() const { return *m_settings; }
void set_error(boost::filesystem::path const& file, error_code const& ec) const
{
m_error_file = file.string();
m_error = ec;
}
error_code const& error() const { return m_error; }
std::string const& error_file() const { return m_error_file; }
void clear_error() { m_error = error_code(); m_error_file.clear(); }
mutable error_code m_error;
mutable std::string m_error_file;
virtual ~storage_interface() {}
disk_buffer_pool* m_disk_pool;
session_settings* m_settings;
};
文件是分片了,但是关于文件分片在程序中的读取优先级,则主要在torrent_handle中处理
torrent_handle
torrent_handle中对文件下载顺序影响最大的一个函数就是set_piece_deadline()函数了,这个函数给每个片段设置一个截止期限,libtorrent会在截止期限终止之前去尝试下载这个片段.而piece_priority() prioritize_pieces() piece_priorities()则设置了每个片段的优先级(注意是和其available有关的).