ltsession(會話)是最核心的,種子只有加入進去方可下載上傳等動作。session_handle: 主要負責對session的操作。

torrent_handle: 主要負責對種子的操作以及狀態查詢。

torrent magnet磁力種子搜索_BT磁力搜索引擎

session.pop_alerts(): 可以獲取從上次調用以來的所有新警報的列表。每種特定類型的警報可能包括特定于消息類型的附加狀態。所有警報都實現一個message()函數,該函數輸出警報消息的相關信息。這可以方便地記錄事件。

下面先針對各小功能進行代碼實現,最好再整合一個完整的代碼例子

1. 添加種子/磁力(并下載)lt::session ses; //定義session對象lt::add_torrent_params p = lt::parse_magnet_uri("magnet:?xt=urn:btih:......");// 解析磁力鏈接p.save_path = ".";// 設置保存到當前目錄lt::torrent_handle h = ses.add_torrent(p); //添加到session,并獲得其句柄(該句阻塞執行,有結果才返回)lt::session ses; //定義session對象auto ti = std::make_shared<lt::torrent_info>(torrentFilePath); //通過種子文件 定義 torrent_info對象lt::add_torrent_params p; p.ti = ti;//這兒很重要, 必須要中torrent_info對象傳遞進來p.save_path = ".";p.userdata = static_cast<void*>(new std::string(torrentFilePath)); //這根據自己需要而定具體數據ses.async_add_torrent(std::move(p)); //這兒是異步調用, 調完就離開,獲取不到 session_handle

以上為 通過磁力鏈接 和 torrent 種子這兩種方式添加到session的簡單例子。torrent_info類又諸多構造函數,對象定義方法也因此非常多,根據需要選擇合適的。

另外添加種子到session的方法有 同步調用(阻塞耗時)和異步調用(立即返回)兩種,也根據需要選擇。

2. 警報

直接上代碼講解

for (;;) {std::vector<lt::alert*> alerts;ses.pop_alerts(&alerts);//獲取自上次調用以來的新警報列表for (lt::alert const* a : alerts) {//遍歷處理自己需要的警報。std::cout << a->message() << std::endl; //打印警報信息// 下面是針對各種類型的警報進行處理例子if (lt::alert_cast<lt::torrent_finished_alert>(a)) {goto done;}if (lt::alert_cast<lt::torrent_error_alert>(a)) {goto done;}}std::this_thread::sleep_for(std::chrono::milliseconds(200));}done:std::cout << "done, shutting down" << std::endl;

我們可以用‘警報掩碼’抓取我們關心的警報(有很多種類),如下 :

lt::settings_pack pack;pack.set_int(lt::settings_pack::alert_mask// 設置掩碼 關心 下面三種掩碼, lt::alert::error_notification| lt::alert::storage_notification| lt::alert::status_notification);lt::session ses(pack);

警報掩碼 之 掩碼類別

警報類型

3. 更新配置選項

session啟動后,可以通過調用 apply_settings() 更新配置,

lt::settings_pack pack;pack.set_int(lt::settings_pack::alert_mask,lt::alert::status_notification);ses.apply_settings(pack);// 更新配置 alert_mask

雖然這樣可以更新配置,但有些設置最好在啟動會話之前設置,比如 listen_interfaces,以避免出現競爭條件。如果使用默認設置啟動會話,然后立即更改它們,則仍會有一個應用默認設置的窗口。

更改設置可能會觸發偵聽套接字關閉和重新打開,并發送NAT-PMP、UPnP更新。因此,將設置更新批處理到單個調用中通常是一個好主意。

4. 獲取種子狀態for(;;){ses. post_torrent_updates();// 發送 alert::status_notificationstd::this_thread::sleep_for(250);std::vector<lt::alert*> alerts;ses.pop_alerts(&alerts);//獲取自上次調用以來的新警報列表for (lt::alert const* a : alerts) {//處理 state_update_alertif (state_update_alert* p = alert_cast<state_update_alert>(a)) {// 包含自上次調用以來更新的種子狀態std::vector<torrent_status> status = p->stauts;......}}....}5. 恢復種子

恢復下載時,BT引擎必須恢復正在下載種子的狀態,特別是文件的哪些部分已下載, 有兩種方法可以做到這一點:

(1) 從磁盤讀取已下載文件片段,并將其與預定的哈希值進行比較。

(2) 保存已下載的片段(和部分片段)的狀態到磁盤,并在恢復時重新加載。

如果添加種子時沒有提供恢復數據,那么libtorrent將默認使用上面第1點。

libtorrent有提供函數,實現保存恢復數據的功能:

 torrent_handle.save_resume_data(torrent_handle::save_info_dict) ;

調用該函數的時機:

(1). 人為地選中某個種子,操作‘保存恢復數據’。

(2). 每個種子加載完成時。

(3). 關閉session之前。

注意:調用該函數并沒將恢復數據保存到磁盤上面, 調用該函數后實際上會發出警報:

save_resume_data_alert(若成功) 或
save_resume_data_failed_alert(若失敗)

else if (save_resume_data_alert* p = alert_cast<save_resume_data_alert>(a)){torrent_handle h = p->handle; // 獲取torrent_handle,目的是獲取torrent_statusauto const buf = write_resume_data_buf(p->params); // p->params為 add_torrent_params 類型torrent_status st = h.status(torrent_handle::query_save_path);save_file(resume_file(st.info_hash), buf); //保存恢復數據到名為info_hash(長度40)的文件中}else if (save_resume_data_failed_alert* p = alert_cast<save_resume_data_failed_alert>(a)){// 如果不需要保存恢復數據,可以不打印錯誤信息return p->error == lt::errors::resume_data_not_modified;}6. session對象的析構銷毀

默認情況下會話析構函數會被阻塞。關閉時,需要聯系追蹤器以停止種子,其他未完成的操作需要取消。關閉有時可能需要幾秒鐘,主要是因為跟蹤器沒有響應(和超時)以及DNS服務器沒有響應。DNS查找在失控時尤其難以中止。

為了能夠異步地開始銷毀等待,可以調用session::abort()。它返回一個session_proxy對象,它是一個句柄,用于在銷毀會話狀態時保持會話狀態。它故意不提供任何會話操作,因為它正在關閉。

擁有session_proxy對象后,會話析構函數不會阻塞。但是session_proxy析構函數卻將被阻塞。

這可用于并行關閉多個會話或應用程序的其他部分。

7. 完整實例#include <iostream>#include <thread>#include <chrono>#include <fstream>#include <libtorrent/session.hpp>#include <libtorrent/add_torrent_params.hpp>#include <libtorrent/torrent_handle.hpp>#include <libtorrent/alert_types.hpp>#include <libtorrent/bencode.hpp>#include <libtorrent/torrent_status.hpp>#include <libtorrent/read_resume_data.hpp>#include <libtorrent/write_resume_data.hpp>#include <libtorrent/error_code.hpp>#include <libtorrent/magnet_uri.hpp>using clk = std::chrono::steady_clock;//返回 種子各種狀態的名稱char const* state(lt::torrent_status::state_t s){switch(s) {case lt::torrent_status::checking_files: return "checking";case lt::torrent_status::downloading_metadata: return "dl metadata";case lt::torrent_status::downloading: return "downloading";case lt::torrent_status::finished: return "finished";case lt::torrent_status::seeding: return "seeding";case lt::torrent_status::allocating: return "allocating";case lt::torrent_status::checking_resume_data: return "checking resume";default: return "<>";}}int main(int argc, char const* argv[]) try{if (argc != 2) {std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;return 1;}lt::settings_pack pack;pack.set_int(lt::settings_pack::alert_mask, lt::alert::error_notification| lt::alert::storage_notification| lt::alert::status_notification);lt::session ses(pack);clk::time_point last_save_resume = clk::now();// 從磁盤加載恢復數據,并在添加磁力鏈接時傳遞它std::ifstream ifs(".resume_file", std::ios_base::binary);ifs.unsetf(std::ios_base::skipws);std::vector<char> buf{std::istream_iterator<char>(ifs) , std::istream_iterator<char>()};lt::add_torrent_params p = lt::read_resume_data(buf);lt::add_torrent_params magnet = lt::parse_magnet_uri(argv[1]);if (p.info_hash != magnet.info_hash) { //判斷恢復數據和磁力鏈接是否匹配,以磁力鏈接為準p = std::move(magnet);}p.save_path = "."; // 保存到當前目錄ses.async_add_torrent(std::move(p));lt::torrent_handle h;//當我們收到add_torrent_alert時,我們設置它for (;;) {std::vector<lt::alert*> alerts;ses.pop_alerts(&alerts);for (lt::alert const* a : alerts) {if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {h = at->handle;}// 如果收到 torrent_finished_alert或torrent_error_alert,則退出程序if (lt::alert_cast<lt::torrent_finished_alert>(a)) {h.save_resume_data();goto done;}if (lt::alert_cast<lt::torrent_error_alert>(a)) {std::cout << a->message() << std::endl;goto done;}// resume data準備就緒,保存到磁盤文件if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {std::ofstream of(".resume_file", std::ios_base::binary);of.unsetf(std::ios_base::skipws);auto const b = write_resume_data_buf(rd->params);of.write(b.data(), b.size());}if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {if (st->status.empty()) continue;// 因為我們只有一個種子,所以我們知道 這個status是哪個種子的lt::torrent_status const& s = st->status[0];std::cout << "r" << state(s.state) << " "<< (s.download_payload_rate / 1000) << " kB/s "<< (s.total_done / 1000) << " kB ("<< (s.progress_ppm / 10000) << "%) downloadedx1b[K";std::cout.flush();}}std::this_thread::sleep_for(std::chrono::milliseconds(200));// post state_update_alert 更新種子輸出狀態ses.post_torrent_updates();// 每30秒保存恢復數據一次if (clk::now() - last_save_resume > std::chrono::seconds(30)) {h.save_resume_data();last_save_resume = clk::now();}}// 理想情況下,我們應該在這里保存恢復數據done:std::cout << "ndone, shutting down" << std::endl;}catch (std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;}