libtorrent+API+手册(中文版) - 图文
更新时间:2023-10-05 02:41:01 阅读量: 综合文库 文档下载
libtorrent API 文档(中文版)
作者:Arvid Norberg, arvi@rasterbar.com 翻译:小朋友软件, xpysoft@163.com
文档原处:http://www.rasterbar.com/products/libtorrent/manual.html version:0.14.5 内容列表 ·总观
·网络原语 ·session
session() o ~session()
o pause() resume() is_paused() o abort()
o add_torrent() o remove_torrent()
o find_torrent() get_torrents()
o set_upload_rate_limit() set_download_rate_limit()
upload_rate_limit() download_rate_limit() o set_max_uploads() set_max_connections() o num_uploads() num_connections() o set_max_half_open_connections()
max_half_open_connections()
o load_asnum_db() load_country_db() int as_for_ip() o load_state() state() o set_ip_filter() o get_ip_filter() o status()
o get_cache_status() o get_cache_info()
o is_listening() listen_port() listen_on()
o pop_alert() set_alert_mask() wait_for_alert()
set_alert_queue_size_limit() o add_extension()
o set_settings() set_pe_settings()
o set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()
o peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy() o start_dht() stop_dht() set_dht_settings() dht_state() o add_dht_node() add_dht_router() o start_lsd() stop_lsd() o start_upnp() stop_upnp() o start_natpmp() stop_natpmp()
? entry
o
integer() string() list() dict() type() o operator[] o find_key()
? torrent_info
o torrent_info() o add_tracker()
o files() orig_files() o rename_file()
o begin_files() end_files() rbegin_files() rend_files() o num_files() file_at() o map_block() o map_file()
o url_seeds() add_url_seed() o trackers()
o total_size() piece_length() piece_size() num_pieces() o hash_for_piece() hash_for_piece_ptr() info_hash() o name() comment() creation_date() creator() o priv() o nodes() o add_node()
o metadata() metadata_size()
? torrent_handle
o piece_priority() prioritize_pieces() piece_priorities() o file_priority() prioritize_files() file_priorities() o file_progress() o save_path() o move_storage() o rename_file()
o get_storage_impl() o force_reannounce() o scrape_tracker() o connect_peer() o name()
o set_ratio()
o set_upload_limit() set_download_limit() upload_limit() download_limit()
o set_sequential_download() is_sequential_download() o set_peer_upload_limit() set_peer_download_limit() o pause() resume() is_paused() o force_recheck() o clear_error()
o resolve_countries() o is_seed()
o is_auto_managed() auto_managed()
o
? ? ? ? ? ?
? ? ? ? ?
?
?
has_metadata()
o set_tracker_login()
o trackers() replace_trackers()
o add_url_seed() remove_url_seed() url_seeds() o queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom() o use_interface() o info_hash()
o set_max_uploads() set_max_connections() o save_resume_data() o status()
o get_download_queue() o get_peer_info() o get_torrent_info() o is_valid() torrent_status peer_info
session_settings pe_settings proxy_settings ip_filter
o ip_filter() o add_rule() o access()
o export_filter() big_number bitfield hasher
fingerprint
UPnP and NAT-PMP
o add_mapping o delete_mapping o router_model() free functions
o identify_client() o client_fingerprint() o bdecode() bencode() o add_magnet_uri() o make_magnet_uri() alerts
o external_ip_alert o listen_failed_alert o portmap_error_alert o portmap_alert
o
file_error_alert o file_renamed_alert
o file_rename_failed_alert o tracker_announce_alert o tracker_error_alert o tracker_reply_alert o dht_reply_alert
o tracker_warning_alert o scrape_reply_alert o scrape_failed_alert o url_seed_alert o hash_failed_alert o peer_ban_alert o peer_error_alert
o invalid_request_alert o torrent_finished_alert o performance_alert
o metadata_failed_alert o metadata_received_alert o fastresume_rejected_alert o peer_blocked_alert o storage_moved_alert
o storage_moved_failed_alert o torrent_paused_alert o torrent_resumed_alert o save_resume_data_alert
o save_resume_data_failed_alert o dispatcher
? exceptions
o invalid_handle o duplicate_torrent o invalid_encoding o type_error
o invalid_torrent_file
? storage_interface
o initialize() o has_any_file() o read() o write()
o move_storage()
o verify_resume_data() o write_resume_data() o move_slot() o swap_slots()
o
? ?
? ? ?
?
? ?
swap_slots3() o hash_for_slot() o rename_file() o release_files() o delete_files() magnet links queuing
o downloading o seeding fast resume
o file format threads
storage allocation
o sparse allocation o full allocation o compact allocation extensions
o metadata from peers o HTTP seeding filename checks acknowledgments
o
概述
libtorrent库的接口由少量几个类组成。最主要的类是session类,它有一个为所有任务服务的主循环.
基本的用法如下:
? 创建一个session
? 打开 扩展(查看 add_extension()).
? 打开 DHT, LSD, UPnP, NAT-PMP 等 (查看 start_dht(), stop_dht(),
set_dht_settings(), dht_state(), start_lsd(), stop_lsd(), start_upnp(), stop_upnp 和start_natpmp(), stop_natpmp()); ? 解析 .torrent文件 并且把它们加入到session中(查看 bdecode(),
bencode()和add_torrent()) ? 主循环(查看 session)
? 查询事务句柄, 得到下载进度(查看torrent_handler类) ? 查询事务,得到下载信息。
? 在运行时从事务中增加或者删除下载任务。 ? 主循环(查看 session)
? 为任务句柄保存新下载的数据(可以查看save_resume_data()函数) ? 析构事务对象
每个类和函数在这个手册中被描述到
关于如何创建任务种子文件的描述,但看make_torrent函数 网络原语
在libtorrent名字空间中有一些宏定义,这些定义从asio名字空间中选出。它们是:
typedef asio::ip::address address;
typedef asio::ip::address_v4 address_v4; typedef asio::ip::address_v6 address_v6; using asio::ip::tcp; using asio::ip::udp;
这些在
这些端点类型在libtorrent库中被使用.一个端点是一个地址与相关端口的结合。
关于这些类型的文档,请查看相关asio的文件说明.
session
事务类有如下一些概要:
class session: public boost::noncopyable {
session(fingerprint const& print = libtorrent::fingerprint( \
, int flags = start_default_features | add_default_plugins);
session(
fingerprint const& print
, std::pair
, char const* listen_interface = 0
, int flags = start_default_features | add_default_plugins);
torrent_handle add_torrent(add_torrent_params const& params);
void pause(); void resume();
bool is_paused() const;
session_proxy abort();
enum options_t {
none = 0,
delete_files = 1 };
enum session_flags_t {
add_default_plugins = 1, start_default_features = 2 };
void remove_torrent(torrent_handle const& h, int options = none);
torrent_handle find_torrent(sha_hash const& ih); std::vector
void set_settings(session_settings const& settings); void set_pe_settings(pe_settings const& settings);
void set_upload_rate_limit(int bytes_per_second); int upload_rate_limit() const;
void set_download_rate_limit(int bytes_per_second); int download_rate_limit() const; void set_max_uploads(int limit); void set_max_connections(int limit);
void set_max_half_open_connections(int limit); int max_half_open_connections() const;
void set_peer_proxy(proxy_settings const& s); void set_web_seed_proxy(proxy_settings const& s); void set_tracker_proxy(proxy_settings const& s);
proxy_settings const& peer_proxy() const; proxy_settings const& web_seed_proxy() const; proxy_settings const& tracker_proxy() const;
int num_uploads() const; int num_connections() const;
bool load_asnum_db(char const* file); bool load_country_db(char const* file); int as_for_ip(address const& adr);
void load_state(entry const& ses_state); entry state() const;
void set_ip_filter(ip_filter const& f); ip_filter const& get_ip_filter() const;
session_status status() const;
cache_status get_cache_status() const;
bool is_listening() const;
unsigned short listen_port() const; bool listen_on(
std::pair
std::auto_ptr pop_alert();
alert const* wait_for_alert(time_duration max_wait); void set_alert_mask(int m);
size_t set_alert_queue_size_limit(size_t queue_size_limit_);
void add_extension(boost::function<
boost::shared_ptr
void start_dht(); void stop_dht();
void set_dht_settings(
dht_settings const& settings); entry dht_state() const;
void add_dht_node(std::pair
void add_dht_router(std::pair
void start_lsd(); void stop_lsd();
upnp* start_upnp(); void stop_upnp();
natpmp* start_natpmp(); void stop_natpmp(); };
一旦它被创建,事务对象将会启动主线程来处理所有的工作。如果没有任何任务提交给主线程的话,主线程将会空闲.
session()
session(fingerprint const& print
= libtorrent::fingerprint(\
, int flags = start_default_features | add_default_plugins);
session(fingerprint const& print
, std::pair
, int flags = start_default_features | add_default_plugins); 如果第一个重载函数中指纹函数被省略了,客户端将获得一个表示库版本的默认指纹, 这个指纹是个将用在同步id中来辨认客户与客户的版本的一个短字符串,关于指纹的更多细节可以查看fingerprint类。这个构造函数只是提供一个指纹,但并不为事务打开一个监听端口,如果想打开端口,必须调用listen_on函数。 第二个构造函数,会提供一个监听端口的范围,监听接口,指纹会自动监听提供的接口。并于参数的更多细节,可以参考listen_on()函数。
标志参数可以用来启动默认参数(比如upnp&nat-pmp)和默认插件
(ut_metadata, ut_pex, smart_ban).默认情况下是启动这些。如果不想它们启动,传0给flags参数即可。
~session()
session的析构将会通知所有的服务器我们的任务已关闭。如果一些服务器正在下载,它们将会延时。这些操作在事务对象析构完才会操作完。所以,只有关掉所有接口才析构所有的事务。因为它需要几秒钟才结束。这个超时时间可以用set_settings()函数设置。 pause() resume() is_paused()
void pause(); void resume();
bool is_pause() const;
除非这些任务能被自动管理机制恢复,停止一个事务与停止事务中的任务一样.恢复时将重新装载这些任务到它们以前停止的状态。因为事务停止时的状态与任务停止时的状态是分开的。如果事务停止或者任务停止的话,任务将会处于非活动状态。 abort()
session_proxy abort()
如果你想异步地析构事务,你能申请一个事务析构代理。如果你不这样做,当服务器被连接时,事务对象的析构过程将来阻塞.当析构事务时,如果给事务提供一个代理,析构过程将不会被阻塞,而是关掉事务,代理的析构将会同步此线程。所以,当调用事务析构函数后,只有等到事务代理的析构函数被调用时,事务才会真正开始析构。事务析构代理不会对事务作任何操作(因为事务被关闭时,事务上不允许有任何操作).咋一合法的操作就是调用析构函数: class session_proxy { public:
session_proxy(); };
add_torrent()
typedef storage_interface* (&storage_constructor_type)( file_storae const&, file_storage const*, fs::path const&, file_pool&);
struct add_torrent_params {
add_torrent_params(storage_constructor_type s);
~session_proxy();
boost::intrusive_ptr
std::vector
bool auto_managed;
bool duplicate_is_error;
storage_constructor_type storage; void* userdata; };
torrent_handle add_torrent(add_torrent_params cosnt& params); 通过add_torrent()函数,就可以为事务增加任务,这样你就给了session所有的参数。
save_path是仅有的一个规定的参数,它是你的文件需要存放的目录。你也要指定ti(种子文件)或者info_hash(种子的哈希信息).如果你指定了哈希信息,任务文件将会中对等点上下载,这就要求它们支持元数据扩展。当元数据扩展工作时,libtorrent为将使扩展设置为使能状态。
(TORRENT_DISABLE_EXTENSIONS 不被定义). 这个扩展有一个可选的名字参数。如果没有名字给这个参数的话,它可能是0.如果这个名字不是0, 则只要扩展没有元数据的话,则名字将用在任务中.具体的可查看torrent_handle::name 如果任务没有服务器的话,就需要DHT来发现对等点(peers), 服务器的地址可能是0, 否则你必须指定一个查找任务的服务器地址.
如果你试图增加的任务已在事务中存在(正在队列中等待检查,正在被检查,或者正在下载) add_torrent()函数将会抛出从std::exception派生的异常,除非duplicate_is_error参数被设置为假。在那种情况下,add_torrent函数将返回已存在任务的句柄。
如果着能快速更新下载的数据的话, 可选的参数resume_data将会被给出。通过调用torrent_handler上的save_resume_data函数,fast_resume 数据将会从运行的任务中取出。具体可查看fast_resume.通过使用
std::vector::swap()函数。正在传递的容器将会交换进运行的任务事例中。
存储模式的参数涉及这个任务的储布局。有三种模式: 分解存储模式
所有的片将所写到它们属于并且分解文件将用到的地方。这是推荐的,是一种默认模式。 分配存储模式
除了文件将会在开始时被截断外,其它与分解存储模式相同.因为文件系统支持分解文件,它会与分解模式相同,如果文件系统不支持分解文件,libtorrent库将会为文件分配数据。Mac文件系统HFS+不支持分解文件,它将会分配长度为0的文件。 紧缩存储模式
这种模式下,存储将会随着片的下载而增长,并且所有的片将会在整个任务都下载后被重新组织在正确的地方. 更多的信息,可以查看storage_allocation
paused参数是用来指定是否任务将会在中止的状态被启动的标志。例如,只有任务被恢复时,任务才会连接服务器或者任意一个端点.这通常可以避免在启动任务时,设置配置的选项存在竟争条件。
当恢复的数据到来时,恢复数据将来保存,同时任务的中止状态参数将会重载你在这里传入的中止状态。
如果auto_managed参数为真时,任务将会放入队列,被libtorrent库开始或生成种子。同时,任务应以中止的方式启动。默认的队列顺序与任务加入的顺序相同。它们也以那种顺序下载,更多细节可以查看queuing.
如果传入了恢复数据,恢复数据将被存储,任务的auto_managed状态将重载这里传入的auto_managed 状态。
存储将会用来定制数据的存储方式。默认的存储将会简单地写到它属于的文件,但是它可能重载地存放所有的数据到单个的文件或者在磁盘上加载这些数据。storage_interface需要配合一个客户存储而执行,更多细节可以查看storage_interface,
userdata参数是可选的,并且将会被传递到扩展构造函数中(可查看 add_extension())
add_torrent()返回的Torrent_handle可以用来获得任务的处理信息,它的对等点等。也可以用来结束一个任务。 remove_torrent()
void remove_torrent(torrent_handle const & h, int options = none);
remove_torrent将会关闭与当前任务所连接的对等点连接,并要告诉其它对等点自已已停止。可选的第二个参数用来删除任务所下载的文件。为了达到此目的,可传入session::delete_files,任务的删除是异步的。当一个任务被删除后,向事务中加入一个相同的任务,libtorrent库做不作出这种检查,所以不会抛出异常。
find_torrent() get torrents()
torrent_handle find_torrent(sha_hash const& ih); std::vector
find_torrent()在给定哈希信息中查找一个任务。如果此时在事务中有一个任务,函数会返回一个torrent_handle;如果找不到,将返回一个非法的torrent_handle.
可以调用torrent_handle::is_valid()来查看是否任务被找到。
get_torrents()返回将返回一个句柄容器。它里面存放着事务中当前所有任务的任务句柄。
set_upload_rate_limit()
set_download_rate_limit() upload_rate_limit() download_rate_limit()
void set_upload_rate_limit(int bytes_per_second); void set_download_rate_limit(int bytes_per_second); int upload_rate_limit() const; int download_rate_limit() const;
set_upload_rate_limit()设备每秒钟允许被传到对等点的最大字节。带宽被分配到所有的对等点。如果没有限制上传速率,你可以把它设备为-1(默认)。 同样地,set_download_rate_limit()设置下载速率。download_rate_limit()与upload_rate_limit()将会返回以前设置的值。
上传与下载速度限制将默认地不在局域网上设置生效。要改变这种情况,可以查看session_settings::ignore_limits_on_local_network
set_max_uploads() set_max_connections() void set_max_uploads(int limit); void set_max_connections(int limit);
这些函数将会设置未阻塞对等点的数量与打开连接的数量的限制。连接的数量至少设为每个任务两个连接。所以如果你设了一个太小的连接限制,但打开很多任务,这个限制将会忽略。上传的数理至少为一个任务一个上传. num_uploads() num_connections() int num_uploads() const; int num_connections() const;
返回未检验的对等点的当前数量和连接的数量。 set_max_half_open_connetions() max_half_open_connections()
void set_max_half_open_connections(int limit); int max_half_open_connections() const;
第一个函数设置了连接对等点的最大半开连接数量。半开状态是当已调用了connect()函数,但连接还没有完成,也没有报告失败时的状态。
Windows XP sp2设备了一个默认的,系统宽度的,半开连接的数目为10.所以,这个限制可以更好的与系统上的网络应用工作在一起。默认的情况下没有限制,可以设置为-1。当限制了同时连接尝试的数量时,对等点将会放入队列,以等待它们连接的到来。
max_half_open_connections()返回了设置限制。这个限制在windows上默认为8
load_asnum_db() load_country_db() int as_for_ip() bool load_asnum_db(char const* file); bool load_country_db(char const* file); int as_for_ip(address const& adr);
当TORRENT_DISABLE_GEO_IP被定义时,这些函数被不被调用。它们将分别得到一个给MaxMind ASN database 和MaxMind GeoIP database的路径。这将用来查找AS 和对等点的归属.
as_for_ip 返回指定ip地址的AS数。如果ip不在数据库中,并且ASN数据库没有被加载,将返回0. load_state() state()
void load_state(entry const& ses_state); entry state() const;
这些函数加载并保存事务的状态。现在,存储的咋一状态是ASes的最大下载速度。这个对应关系决定连接对等点的顺序。 set_ip_filter()
void set_ip_filter(ip_filter const& filter);
此函数用来设置一个用来拒绝或者接受进入与在原始ip地址的外出连接进滤器。默认的过滤器将允许任意ip地址。为了设置一个接受与不接受ip的规则,可以查看ip_filter
当一个对等点因为ip过滤器被阻塞时,peer_blocked_alert将会生成。
get_ip_filter() ::
ip_filter const& get_ip_filter() const;
返回事务中当前ip_filter, 可以查看ip_filter了解更多细节 status()
session_status status() const;
status()返回事务的带宽统计与状态。session_status结构体有如下成员: struct session_status {
bool has_incoming_connections;
float upload_rate; float download_rate;
float payload_upload_rate; float payload_download_rate; size_type total_download; size_type total_upload;
size_type total_redundant_bytes; size_type total_failed_bytes;
size_type total_payload_download; size_type total_payload_upload;
int num_peers; int num_unchoked;
int allowed_upload_slots;
int dht_nodes;
int dht_cache_nodes; int dht_torrents; int dht_global_nodes; };
只要在监听套接口上没有连接请到来,那么has_incoming_connections就被设置为假。第次当改为监听端口时,这个参数也会重设为假.
上传速率,下载速率,xxx 和xxx 都是从任务中计算出的下载或上传速率。承载版本是只承载下载的速率。
total_download与total_upload是从所有任务中得到的所有上传与下载字节。total_payload_download与total_payload_upload是一样的除非payload被考虑到。
total_redundant_bytes是多收的字节数。当一个对等点的请求超时或者从不同的对等点上被请求时,又从原来的对等点上接收了数据,就有可能接收了多余的数据。为了使这个参数的值更小,可以增加请求超时时间或者文件数据片的长度。
total_failed_bytes是下载下来了的数据但后来在hash-check失败了的数据。
num_peers事务中含有的对等点连接的总数。这包括没有发送握手信息的连接请求或者没有完成tcp连接的外出连接。因为请求的连接可能有没有设置一个任务,所以这个值可能比所有任务的所有对等点之共要大一些。 num_unchoked是现在没有阻塞的对等点的当前数。 allowed_upload_slots是当前允许的没有阻塞的对等点数目
dht_nodes, dht_cache_nodes和dht_torrents只有创建DHT支持的事务时才有用。当DHT没有运行时,它们都设为0. 当DHT运行时,dht_nodes设为路由表里节点的数目. 这个值只包括活动的节点。而不包括缓冲节点。
Dht_cache_nodes被设置为点缓冲区中节点的数目。这些节点将用来代替路由表中的固定结点,以防止对等点没有反应
dht_torrents 是在某一时刻被dht跟踪的任务的数目
dht_global_nodes是在DHT网络中所有节点数目的估计值 get_cache_status()
cache_status get_cache_status() const; 返回事务的磁盘缓冲的状态 struct cache_status {
size_type blocks_written; size_type writes;
size_type blocks_read; size_type blocks_read_hit; size_type reads; int cache_size;
int read_cache_size; };
blocks_written 是当事务开始时,写向磁盘的16KB的块的所有数目 writes为当事务开始时写操作的所有数目
(blocks_written – writes)/blocks_written代表了每个总写操作的 已保存写操作的数目。比如。写缓冲的命中缓冲比例
blocks_read是从任务引擎(在对等点上)被请求的块数目.它在磁盘或缓冲中被处理
blocks_read_hit是在缓冲中被处理的块个数
blocks_read_hit/blocks_read 的比例是读缓冲的缓冲命中比率。 cache_size是当前磁盘缓冲中16K块的数目,它包括读和写缓冲 read_cache_size是坊缓冲中16K块的数目 get_cache_info()
void get_cache_info(sha1_hash const& ih
, std::vector
int piece;
std::vector
enum kind_t { read_cache = 0, write_cache = 1 }; kind_t kind; };
piece是缓冲入口的片索引。
在每个数据片中的每个块中,blocks有一个入口。True 代表在碰盘缓冲的块有数据,否则没有数据。
last_use是当一个块被最后写到片中的时间。片越旧,那么被清出碰般的可能性越大。
kind指明是否当前片是读缓冲或者写缓冲中的一部分。
is_listening() listen_port() listen_on()
bool is_listening() const;
unsigned short listen_port() const; bool listen_on(
std::pair
is_listening() 将可以断定事务是否成功地打开一个监听的端口。如果它没有打开,这个函数将返回假. 并且也可以使用listen_on()来做尝试.
listen_port()返回是最终成功监听的端口.因为你仅传了一个端口范围 组构造函数和listen_on(), 若要知道成功运用的端口。你不得不让session使用这个函数。
listen_on()将改变监听的端口或者监听的接口。如果事务已经监听的端口,套接字将会关掉并且新的套接字将会被打开,配置将使用最新的。这个端口范围 是将会监听的端口范围。如果第一个端口打开失败,那么它将会尝试下一个端口。接口参数可能会设为0, 此时,操作系统会决定监听哪个端口。否则必须以你监听套接字的接口ip地址进行绑定。如果绑定成功,会返回成功,否则失败。如果失败,会产生一个合适的警告异常.
接口参数可能是个主机名,它将会解析到你想监听的设备。 如果你也启动了DHT, 这是在调用listen_on()函数后的好办法,因为默认的DHT监听端口与tcp监听套接字是相同的。如果你先启动了DHT, 那么系统会估计tcp端口是空闲的并且同时在那个端口上打开udp套接字.接着,当调用 Listen_on()函数后,它将知道tcp端口被占用。这会导致DHT与任务套接口监听不同的端口.如果当listen_on被调用时DHT是活动的,,如果被设备成DHT与tcp用同一个端口,并且如果listen_on调用失败,那么udp端口会绑定到新的端口。
将DHT与任务套接口使用同一个端口是一个很办法。那是因为这样能很好的提高性能。在windows上加速与其它对等点的连接的办法就是使用DHT ping 包ping所有的对等点,如果有响应,则连过去。因为在xp service pack 2上同一时间创建套接字有限制,所认只能在同一时间连几个对等点。 pop_alert() set_alert_mask() wait_for_alert() set_alert_queue_size_limit()
std::auto_ptr pop_alert();
alert const* wait_for_alert(time_duration max_wait); void set_alert_mask(int m);
size_t set_alert_queue_size_limit(size_t queue_size_limit_);
pop_alert()可以告诉事务是否一些错误或者事情已经发生。使用
set_alert_mask()可以过滤掉一些通过pop_alert()接收到的警告。具体的关于警告的信息,可查看alerts.
当警告到来之前, wait_for_alert 将会一直阻塞,除非最大等待时间到了。如果由于超时原因,wait_for_alert返回了,但没有警告,它会返回0.如果有警告,那么警告的指针将会返回。这个警告将不会被弹出,随后所有的
wait_for_alert调用都会得到同一个结果,直到调用pop_alert函数弹出警告。这可以使警告处理机制与阻塞的调用独立开来,此时既会处理也会独立的弹出警告
set_alert_queue_size_limit()能指定一个多少个警告将会等待。如果这个限制设置了,在调用pop_alert()弹出警告之前,新的到来的警告将不会被接收。默认值为1000. add_extension()
void add_extension(boost::function<
boost::shared_ptr
boost::shared_ptr
允许对等点从系统时直接下载元数据(即.torrent 文件)。这个就可以加入到只有一个服务器和info-hash的系统中.
#include
与metadata 扩展相同,但它能兼容uTorrent.
#include
ses.add_extension(&libtorrent::create_ut_metadata_plugin); uTorrent peer exchange
在客户端中交换对等点。
#include
ses.add_extension(&libtorrent::create_ut_pex_plugin);
smart ban plugin
它可以禁止对等点高频地发送脏数据。当有的客户端有问题时,用它可以解决很多问题
#include
ses.add_extension(&libtorrent::create_smart_ban_plugin); set_settings() set_pe_settings()
void set_settings(session_settings const& settings); void set_pe_settings(pe_settings const& settings); 分别设置事务设置和包加密设置。可以查看session_settings 和pe_settiongs的相关细节。
set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()
void set_peer_proxy(proxy_settings const& s);
void set_web_seed_proxy(proxy_settings const& s); void set_tracker_proxy(proxy_settings const& s); void set_dht_proxy(proxy_settings const& s);
当DHT没有启动时,set_dht_proxy函数将不起作用。这些函数为各种不同的连接,bittorrent客户端对等点,种子,服务器,DHT链路设置不同的代理设置。 set_peer_proxy 影响原来的bt对等点。Set_web_seed_proxy只影响网络种子,可以查看HTTP seeding.
set_tracker_proxy仅仅影响HTTP服务器连接(如果代理支持UDP, 比如SOCKS5的话,UDP服务器链接将受到影响)
set_dht_proxy影响DHT消息。因为DHT是通过UDP传输消息的,所以如果代理支持UDP的话,DHT消息会受到影响。
可以查看proxy settings 查看更多的代理设置。 peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy() proxy_settings const& peer_proxy() const; proxy_settings const& web_seed_proxy() const; proxy_settings const& tracker_proxy() const; proxy_settings const& dht_proxy() const; 这个函数返回当前设置的引用。
当DHT没有启动时, Dht_proxy将不起作用
start_dht() stop_dht() set_dht_settings() dht_state()
torrent_info const& get_torrent_info() const;
返回一个与这个任务有关的torrent_info对象有关的常量强用。只要
torrent_handle是合法的,这个引用也是合法的。如果torrent_handle非法或者它没有元数据,将会抛出invalid_handle异常。如果任务启动时没有.torrent文件,任务将会处于一个没有元数据的状态。比如,通过 使用提供的服务器和info-hash扩展。 is_valid()
bool is_valid() const;
如果句柄对应一个合法的任务值,此函数返回真。如果句柄没有初始化或者对应的任务已中止,则会返回假。注意当句柄加入到事务中后,句柄可能为非法的。 通常是因为任务的存储是非法的或者文件名没有允许。(因此打不开或者无法创建).如果有这样的错误发生,file_error_alert会生成并且任务对应的所有句柄可能非法。
torrent_status
它包含如下的域
struct torrent_status {
enum state_t {
queued_for_checking, checking_files,
downloading_metadata, downloading, finished, seeding, allocating };
state_t state; bool paused; float progress; std::string error;
boost::posix_time::time_duration next_announce; boost::posix_time::time_duration announce_interval;
std::string current_tracker;
size_type total_download; size_type total_upload;
size_type total_payload_download; size_type total_payload_upload;
size_type total_failed_bytes; size_type total_redundant_bytes;
float download_rate; float upload_rate;
float download_payload_rate; float upload_payload_rate;
int num_peers;
int num_complete; int num_incomplete;
int list_seeds; int list_peers;
int connect_candidates;
bitfield pieces; int num_pieces;
size_type total_done;
size_type total_wanted_done; size_type total_wanted;
int num_seeds;
float distributed_copies;
int block_size;
int num_uploads; int num_connections; int uploads_limit;
int connections_limit;
storage_mode_t storage_mode;
int up_bandwidth_queue; int down_bandwidth_queue;
size_type all_time_upload; size_type all_time_download;
int active_time; int seeding_time;
int seed_rank;
int last_scrape;
bool has_incoming; };
progress是在[0, 1]中的一个值,它代表了当前任务中任务的进度。它可能正在检查文件或者下载。任务的当前任务在状态成员中,它将是下面的其中一个值: queued_for_checking 任务在队列中等着被检查。但是现在有另一个任务正在被检查。这个任务将等着它的机会。 checking_files 任务没有开始它的下载,正在检验已存在的文件 downloading_metadata 任务正在从对等点上下载元数据。这就默认metadata_tranfer扩展在使用 downloading 任务正在下载。这是很多任务大部门时间都停留的状态。这个进度将显示已下载的文件有多少。 finished 在这种状态下,任务已完成了下载,但仍然有全部的任务。比如一些片被过滤掉并且没有下载。 seeding 这种状态下任务已完成下载并且是一个完全的发送者。 allocating 如果任务以完全分配的模式开始,这就表明任务的存储已被分配了。 当下载时,进度是total_wanted_done/total_wanted. 如果任务被中止,那么paused被设置为true, 否则为假.
如果任务被错误中止,那么error将被设为错误消息,这个消息描述了任务被中止的原因。如果任务没有中止或者不是由于错误而中止,这个串将为空。 next_announce是任务向服务器通知自已的情况的时间。并且通知的间隔是服务器希望任务等待并下次再次通知的时间。
current_tracker是最近的服务器的网址。如果没有服务器的请求,它将设为一个空串。
total_download 和total_upload是下载或者上传到所有对等点,事务,计算的字节数。当一个任务被中止或者再次重启时,事务将会重启。当一个任务中止了,这些计数将设为0.如果希望完成,保持,统计。可以使肜all_time_upload和all_time_download.
total_payload_download和total_payload_upload保存了事务发送与接收的所有字节,但只有实际的装载数据(比如感兴趣的数据),这些区域忽略了协议的一些消耗.
total_failed_bytes是已下载或者在片哈希测试时失败了的字节的数目。换句话说,这就是已下载的垃圾数据。
total_redundant_bytes是已经下载了,但又下载一次的数据。原因是在一定情况下,同样的数据可能被错误地下载了。当libtorrent向对等点发送请求时,对等点没有在一定超时时间内回应,libtorrent将会再请求这个块。另一种可能情况是当库可能再次请求块时,它发送的请求不是以FIFO-order回应的(它将再次请求由于不在顺序之内而跳过的块.这种可能性很小。
pieces是代表我们有的片和我们没有的片的字节掩码。如果任务没有下载或者正在生成种子时,这个指针将设为0.
num_pieces是已经下载的片的数目。它等于std::accumulate(pieces->begin(), pieces->end()).所以必须自已计数。如果想保持一个图表的片一起更新,必须查看从最后时间起是否有数据已更新。 download_rate和upload_rate是这个任务的所有对等点的所有速率。通常比所有对等点的计算与合计有更好的准确性。速率的单位是每秒的字节数。Download_payload_rate和upload_payload_rate分别是承载的所有传输速率,而不是协议计数。这个可能比其它的速率要小一些,但是如果过了很长一段时间后(比如当计算ETA:s), 这个差别将会很大。
num_peers是任务现在连接的对等点的数目。在半开状态的对等点连接中(试着连接),或者为之后试着不统计中,对等点连接正在被排序。当调用get_peer_info()时,这些对等点在对等点队列中是可见的。
如果服务器没有以回应的方式发送坏数据的话,Num_complete和
num_imcomplete被设置成-1.这个数据可能是可选的并且在所有服务器是不激活的。如果这些不是-1,它们就是供下载的对等点的所有数目和仍然在下载任务的对等点数目。
list_seeds和list_peers是我们对等点链表中的供下载源的数目和对等点的所有数目(包括种子源).没有必要连接对等点链表中的所有对等点。它是我们知道的包括我们禁用对等点或者不能连接的对等点等所有的对等点的数目。 connect_candidates 是正准备连接的任务的对等点链表中的对等点数目。比如,相对于最大失败闪次数来说,这些对等点会有更少的连接尝试。如果已经有种子源的话,它不是一个种子源,不会禁用.如果设为0, 就意味着我们不能知道我们尝试着连接的对等点的消息。
total_done下载的文件的所有字节数。在事务的过程中,所有这些不用下载。(也是total_payload_download).
total_wanted_done是已下载的字节数,仅仅计数实际想下载的片数。比如,除了一些已有但过滤后不需要的片。
num_seeds是客户端连接的正在提供下载源的对等点的数目。
distributed_copies 是任务的分发拷贝的数目。注意一份拷同可能在多个对等点上传播。整型部分大家当前客户端连接的对等点中的最少片中有多少份拷贝。分数部分说明了比最少片有更多拷贝的片的共享情况。例如:2.5意味着意味着当前客户端连接的对等点中最稀有的片只有两份拷贝,并且所有片的50%有多于两份拷贝.
如果自身是一个种子源,片采摘对象将以最佳的方式被使用,并且激活的片将不再被追踪。此时分发的拷贝将被设为-1.
block_size是块的长度。一个块是个子片。它是每个请求片要求的字节数 和在专门片信息的比特代表的字节数。(查看get_download_queue()).一般是16K, 但是如果片更大一些的话,它可能会更大一些。
num_uploads是任务中未检验的对等点的数目。
num_connections是这个任务拥有的对等点链接的数目。包括还没有完成bittorrent握手的半开状态的链接。这个值通常<=num_peers.
uploads_limit是任务的上传插口(未阻塞对等点)的限制设置. connections_limit任务的链接数的限制设置。
storage_mode是storage_mode_allocate, storage_mode_sparse或
storage_mode_compact中的一种。想知道当前任务以那一种模式存储。可查看storage allocation.
up_bandwidth_queue和down_bandwidth_queue由于任务速率的限制而正在等待更多带宽的任务里对等点的数目。它能决定从任务取得的速率是否在任务的
void start_dht(entry const& startup_state); void stop_dht();
void set_dht_settings(dht_settings const& settings); entry dht_state() const;
当TORRENT_DISABLE_DHT没有定义时,这些函数将不起作用。Start_dht启动dht结点并且使任务中的服务器服务启动。这种启动的状态是可选的。并且包含以前事务的节点与节点id.使用心的条目,dht结点被编码成字典。 nodes
这里一个串链表,每个串是以二进制编码的结点终端。如果这个串长为6字节,那么它将是一个4字节的网络字节序的IPV4地址,后面跟着一个2个字节的,网络字节序的端口。如果 是18个字节,那么它是16个字节的,网络字节序的IPV6地址,后面跟着一个2字节的,网络字节序的端口号。 node-id
以十六进制数,可读写的结点id.
dht_state函数返回dht 结点的当前状态,将它传给start_dht函数将会重新启动dht结点。当启动dht节点时会读取这个状态,所以当关掉事务时,将这个状态保存到磁盘上是个好办法。
如果假设连接的端口已被使用,littorrent库将会抛出异常,为asio::error
stop_dht会停掉dht结点
add_dht_node为路由表增加一个节点,如果bt客户端有自已的引导结点源,则这个新加的dht结点将会被使用。
set_dht_settings设置一些对dht结点有用的一些参数。这个结构有如下一些成员
struct dht_settings {
int max_peers_reply; int search_branching; int service_port; int max_fail_count; };
Max_peers_reply是最大对的 从其它结点回应获取对等点消息的数目。
search_branching 是通知并刷新了路由表后,节点需要发送的当前查询请求的数目。这个参数在kademlia文档里被叫做alpha.
service_port是节点将要监听的udp端口。它默认为0,它表示udp监听端口将与tcp监听端口相同。因为一些NAT 程序保留了一些为已被映射了tcp端口的udp端口。这种映射通过versa. 比如NAT-PMP保护了这些端口.
max_fail_count是在节点从路由表中被删除前,试着连接这个节点的最大尝试次数。如果有已知的工作节点准备代替失败节点,那么它将会被立即代替。 这个限制用来清除那些没有任何结点能代替它们的结点。 add_dht_node() add_dht_router()
void add_dht_node(std::pair
add_dht_router向DHT路由结点表里面增加一个终端。当路由表是空的话,如果事务生成了一个查询操作,那么这些结点将做为备份。路由节点链表中的节点永远不会加入原有的路由表中,这说明这些节点只能用来备用,来保持卸载它们。
你可能总是加的一个例子路由节点是router.bittorent.com start_lsd() stop_lsd() void start_lsd(); void stop_lsd();
这两个函数用来启动与停止本地发现服务。在本地网络中,这些服务将会广播所有非私有任务的哈希信息以在多路到达的同一个群中寻找对等点。 它默认是关掉的。
start_upnp() stop_upnp() upnp* start_upnp(); void stop_upnp();
这些函数用来启动与停止UpnP服务。当启动时,在本地Upnp路由设备上,这个监听端口和DHT端口尝试着打开。
Start_upnp()函数返回的upnp对象能增加或者删除人造的端口映射。通过portmap_alert和portmap_error_alert函数返回映射状态。在stop_upnp被调用之前,这个对象将是合法值。可以查看UpnP 和 NAT-PMP 它默认情况下是关闭的。 start_natpmp() stop_natpmp() natpmp* start_natpmp(); void stop_natpmp();
这些函数用来打开或者关闭NAT-PMP服务。当启动服务时,在路由器上通过NAT-PMP, 这个监听端口和DHT端口尝试着进入监听状态。
start_natpmp函数返回的natpmp对象可以用来增加或者删除人造的端口映射。通过portmap_alert和portmap_error_alert可以返回映射状态。在调用stop_natpmp()之前,这些对象的值是合法的。可以查看Upnp 和NAT-PMP. 默认情况下是关掉的。
entry
在编码体系统结构中,entry类代表一个节点。它可以是一个变量类型,可能是一个链表,一个字典(std::map), 一个整数或者一个字符串。下面是它的声明: class entry {
public:
typedef std::map
enum data_type {
int_t, string_t, list_t,
dictionary_t, undefined_t };
data_type type() const;
entry(dictionary_type const&); entry(string_type const&); entry(list_type const&); entry(integer_type const&);
entry();
entry(data_type t); entry(entry const& e); ~entry();
void operator=(entry const& e);
void operator=(dictionary_type const&); void operator=(string_type const&); void operator=(list_type const&); void operator=(integer_type const&);
integer_type& integer();
integer_type const& integer() const; string_type& string();
string_type const& string() const; list_type& list();
list_type const& list() const; dictionary_type& dict();
dictionary_type const& dict() const;
// these functions requires that the entry // is a dictionary, otherwise they will throw entry& operator[](char const* key);
entry& operator[](std::string const& key);
entry const& operator[](char const* key) const;
entry const& operator[](std::string const& key) const; entry* find_key(char const* key);
entry const* find_key(char const* key) const;
void print(std::ostream& os, int indent = 0) const; };
TODO: finish documentation of entry. integer() string() list() dict() type() integer_type& integer();
integer_type const& integer() const; string_type& string();
string_type const& string() const; list_type& list();
list_type const& list() const; dictionary_type& dict();
dictionary_type const& dict() const;
integer(), string(), list()与dict()函数是返回相应类型的生产者。如果这些接入对象不是你要求的类型,这些生产者将抛出类型错误(它们从std::runtime_error继承).可以通过type()函数得到接入对象的类型。
print()函数可以用来调试。
如果想创建一个给定类型的接入对象,并且希望在它的构造函数中有些类型,接着你可以使用一个非常量的生产者函数来取得一个 能向它赋值的引用。 从任务文件中获得信息的常用代码将如下所示:
entry torrent_file; // ...
// throws if this is not a dictionary
entry::dictionary_type const& dict = torrent_file.dict(); entry::dictionary_type::const_iterator i; i = dict.find(\if (i != dict.end()) {
std::string tracker_url = i->second.string(); std::cout << tracker_url << \}
The following code is equivalent, but a little bit shorter: entry torrent_file; // ...
// throws if this is not a dictionary
if (entry* i = torrent_file.find_key(\{
std::string tracker_url = i->string(); std::cout << tracker_url << \}
为了能更快地从任务文件中获得信息,可以使用torrent_info类。
operator[]
entry& operator[](char const* key);
entry& operator[](std::string const& key);
entry const& operator[](char const* key) const;
entry const& operator[](std::string const& key) const;
这些函数要求接入对象是一个字典,如果不是的话,它们将抛出一个libtorrent::type_error.
operator[]的非常量版本将返回给一定关键字上已存在元素一个引用,或者如果没有给定关键字的元素,那就返回一个新插入的以那个值主关键字值的引用。
operator[]的常量类型仅返回一个给定关键字值的存在元素的引用。如果关键字的值没有找到,那么将抛出一个libtorrent::type_error错误。 find_key()
entry* find_key(char const* key);
entry const* find_key(char const* key) const;
这些函数要求接入对象是一个字典,如果不是,将抛出一个libtorrent::type_error错误.
它将在字典里,查找指定关键字的元素。如果元素找不到,就会返回0.如果找到了,就会返回此元素的指针。
torrent_info
以前的libtorrent版本中,这个类也可以用来创建任务文件。现在这个功能移到了create_torrent类中,可以查看make_torrent. torrent_info有如下声明。
class torrent_info {
public:
torrent_info(sha1_hash const& info_hash); torrent_info(lazy_entry const& torrent_file); torrent_info(char const* buffer, int size);
torrent_info(boost::filesystem::path const& filename);
void add_tracker(std::string const& url, int tier = 0); std::vector const& trackers() const;
file_storage const& files() const;
file_storage const& orig_files() const;
void rename_file(int index, std::string const& new_filename); void rename_file(int index, std::wstring const& new_filename);
typedef file_storage::iterator file_iterator;
typedef file_storage::reverse_iterator reverse_file_iterator;
file_iterator begin_files() const; file_iterator end_files() const;
reverse_file_iterator rbegin_files() const; reverse_file_iterator rend_files() const;
int num_files() const;
file_entry const& file_at(int index) const;
std::vector
peer_request map_file(int file_index, size_type file_offset , int size) const;
bool priv() const;
std::vector
size_type total_size() const; int piece_length() const; int num_pieces() const;
sha1_hash const& info_hash() const; std::string const& name() const; std::string const& comment() const; std::string const& creator() const;
std::vector
boost::optional
int piece_size(unsigned int index) const;
sha1_hash const& hash_for_piece(unsigned int index) const; char const* hash_for_piece_ptr(unsigned int index) const;
boost::shared_array
torrent_info()
torrent_info(sha1_hash const& info_hash); torrent_info(lazy_entry const& torrent_file); torrent_info(char const* buffer, int size);
torrent_info(boost::filesystem::path const& filename);
含有一个哈希信息的构造函数将用给定值初始化哈希信息,使其它的域为空。当没有元数源(种子文件)但有下载任务时,这个将会内部使用。一旦元数据从群中下载下来时,元数据将会被litorrent库创建。
含有懒惰接入对象的构造函数将会从给定任务文件中找到的信息中创建torrent_info对象。懒惰接入对象代表了编码文件中的树节点。为了加载一个.torrent文件到一个懒惰接入对象中去,可以使用lazy_bdecode(), 查看 bdecode(), bencode()函数。
带一个缓冲区指针和大小的版本将解码一个.torrent文件并且初始化一个torrent_info对象。
有一个文件名的版本将简单地加载一个torrent文件并且在构造函数中解码,这非常的方便。而应用程序想报告什么错误的细节将不合适。
add_tracker()
void add_tracker(std::string const& url, int tier = 0);
add_tracker()将向announce-list中加一个服务器。这个纽带决定了服务器将被尝试的顺序。更多的信息可以查看trackers() files() orig_files()
file_storage const& file() const;
file_storage const& orig_files() const;
file_storage对象包含如何将片映射到文件的信息。因为一个存储对象需要在没有种子文件的情况下创建,所以file_storate与torrent_file是独立的。当在存储中改文件名字时,为了使存储与任务文件中的存储不同,存储必须在file_storage中生成一份自我拷贝.
orig_files()为任务生成一份原始的文件存储。这将会被网页服务器链接使用到。网页服务器链接必须用源名字请求文件。通过torrent_info::rename_file()可改文件名。
关于file_storage 对象的更多信息,可以查看如何创建任务的独立文档。 rename_file()
void rename_file(int index, std::string const& new_filename); void rename_file(int index, std::wstring const& new_filename); 使用索引为文件取一个新名字。新文件名是file_storage取得的。这个file_storage是files()文件返回,而不是orig_files返回的。 begin_files() end_files() rbegin_files() rend_files() file_iterator begin_files() const; file_iterator end_files() const;
reverse_file_iterator rbegin_files() const; reverse_file_iterator rend_files() const;
这个类需要一些解释。首先,为了取得任务中所有文件的链表,你能使用begin_files(), end_files(), rbegin_files和rend_files(). 这些函数将提供你file_entry类型的标准容器迭代器。
struct file_entry {
boost::filesystem::path path; size_type offset; size_type size;
size_type file_base;
boost::shared_ptr
这个路径是每个文件的全路径。比如,如果它是一个复合的任务,所有的文件有一个目录,有相同的名字,比如torrent_info::name(). 这个文件名对UTF-8编码。
size是文件的大小,offset是在任务中文件偏移的字节数。比如,在链表中所有文件的大小之和.
file_base是存储应该开始的文件的偏移。一般情况下设为0, 所以存储将在文件的开始设置数据。一旦多个文件被映射到同一个文件上,那么为了不同区域不会重叠,file_base应该设为一个偏移。当映射未选择文件到so-called 部分文件之中时。这种情况将会使用。
一旦路径元素是在元数据中找到的准确拷贝,orig_path将会设为0.如果源元数据中的路径没有正确被编码,并且为了被utf-8接受而被改写,那么会用orig_path来代替源字符串。原因是要保持它能使用正确的哈希信息准确再生产块信息。
num_files() file_at()
int num_files() const;
file_entry const& file_at(int index) const;
如果需要文件的索引入口,可以使用num_files()和file_at()函数再用片查找文件. map_block()
std::vector
这个函数将映射一个片索引,片里一个字节的偏移和含有偏移的相应文件的大小。相应文件里,那个片的那些数据将会被存放起来。
文件片的结构如下: struct file_slice {
int file_index; size_type offset; size_type size; };
file_index涉及到文件的索引。为了得到路径与文件名,使用file_at()并且传给它file_index值作为参数。偏移是范围开始的文件的字节偏移,并且大小是范围的字节数。Size+offset永远不会比文件大小大。
map_file()
peer_request map_file(int file_index, size_type file_offset , int size) const;
这个函数将从指定文件中映射一个范围到任务的范围中。File_offset参数是字节的,文件中的偏移,这个文件的起点是0.peer_request的结构是如下: struct peer_request {
int piece; int start;
正在阅读:
libtorrent+API+手册(中文版) - 图文10-05
photoshop实习报告07-12
生物质能发电行业分析05-11
桥面板吊装专项施工方案03-01
2015高中语文4.7《儒林外史》课后习题(含解析)新人教版选修《中国小说欣赏》03-15
个人工作态度检讨书优秀3篇03-22
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- libtorrent
- 中文版
- 手册
- 图文
- API
- 教育戏剧心得
- 河北情暖学子活动总结(上报给团中央)
- STC单片机AD转换程序设计
- 用友erp常用SQL语句
- 养老地产项目考察报告(2014新)
- 中融-融新发展1号集合资金信托计划 24个月
- 针灸学 重点穴位,特殊穴位总结
- (一)画图沟通理念资料
- matlab中图像读写
- 大桥高墩柱施工电梯安全施工方案
- 农村信用社农商银行校园招聘考试笔试题目试卷历年考试真题
- 丰教字68号
- 高等教育心理学 资料2015岗前培训江苏省高校中专院校高校教师资格证终极复习材料
- 2011-2012石景山区七年级上学期数学期末试题及答案
- 翻译基本技巧(课堂版) 2
- CPLEX学习笔记(转)
- 监考与阅卷工作要求
- SDG吸附剂法处理酸性废气净化方案
- 福建电信笔试题整理分享
- 青年教师共同成长教育机制的探索