ses.start_dht()->ses_imp.start_dht(),session_impl是session的實現。跟蹤進去,m_dht = new dht::dht_tracker(m_io_service, m_dht_settings, m_listen_interface.address(), startup_state),session_impl會在start_dht裡面建立一個dht_tracker對象,這裡的參數需要好好看一下,首先是m_io_service,這是boost::asio庫中的一個對象,簡單來說就是提供io服務,每一個利用boost::asio庫的程式都需要擁有這樣一個對象,它負責與作業系統核心的通訊互動,有點類似於手機中的手機卡一樣,雖然手機已經實現了無線通訊的功能,但是,需要手機卡才能加入到虛線網路,才能擷取我們所需的服務。可以看到,這個io_service對象放在session_impl中,意味著一個session只會擁有一個io_service對象。第二個參數相當於dht網路的設定檔。第三個參數為IP地址。第四個參數為攜帶啟動節點資訊的對象。繼續跟蹤,看看dht_tracker在建構函式中會做些什麼事。跟蹤進去後發現,dht_tracker在建構函式中會進行一系列的初始化,那麼我們挑選幾個比較重要的初始化動作,看看這些傳進去的參數最終都作為何用。第一個:m_strand(ios),原來dht_tracker會維護一個boost::asio::strand對象,此對象有何用處。它是用來保證,通過strand對象封裝的threads,對線性有序的執行,主要是用來實現線程同步的,這個先分析到這裡,有時間再去深究一下。第二個: m_socket(ios, udp::endpoint(listen_interface, settings.service_port)),我們看到了傳進來的IP地址,以及dht_setting原來可以初始化一個udp通訊端。第三個:m_dht(bind(&dht_tracker::send_packet, this, _1), settings, read_id(bootstrap)),m_dht何許物也。原來它是一個node_impl對象,這名字取得還真是讓人難以望文生義啊。dht_tracker又要去初始化node_impl對象,我們現在來稍微理一理dht_tracker和node_impl之間的關係,查看一下介面,我們可以直觀的認為dht_tracker是用來管理dht網路和racker通訊的,從實現看來,dht網路的實現需要藉助於node_impl,至於更深層的關係有待接下來的深入跟蹤。來看參數,第一個參數為一個函數對象,我們有必要瞭解一下此函數對象的signature,我借用boost來表達一下:boost::function<void(msg const&)>。直觀來看,此函數對象的作用應該是發送一個msg,啊呀,好像找到了libtorrent資料通訊的一點端倪了,如果沒有猜錯,那麼send_packet裡面應該能找到我們熟悉的socket通訊了,真是不容易啊。帶著激動的心情,我們進去看看。哇哇哇,果然不失所望啊,這裡面別有洞天啊,看到了這麼多熟悉的字眼“token”,“query”,“info_hash”,“ping”,“find_node”,“get_peer”……哈哈,熟悉dht的朋友應該會很興奮,其餘的就不用我說了吧。接下來就有一個問題了,這個函數究竟會被誰調用呢。我們來看看,它把send_packet封裝成一個函數對象,作為node_impl對象的初始化參數傳遞出去,難道此函數會由node_impl來調用。繼續前進。Node_impl對象的建構函式看起來稍微清爽一些,我們一個一個來看,m_settings(settings):這個setting就是dht_setting,傳的夠遠啊,從session一路走來,經過session_impl,dht_tracker,這會又到了這裡,真想它的終點在那裡,此為後話,留為存照。m_id(node_id ? *node_id : generate_id()):這個不用多說,node_id是也。m_table(m_id, 8, settings):哈哈,dht路由表,終於看到你啦。m_rpc(bind(&node_impl::incoming_request, this, _1), m_id, m_table, f):原來node_impl還會去初始化一個rpc_manager對象,哇,又看到了久違的boost::bind了,不用說又有函數對象產生了,看看這次又會有什麼發現。還是用boost::function來表述吧,查證以後原來還是boost::function<void(msg const&)>,我們可以看到這次封裝的是node_impl::incoming_request,那麼裡面又會發生什麼事情呢。會不會又有驚喜啊。來看看吧。哇哇,熟悉的switch..case語句,還用說什麼,無以言表啊……到這裡我們找到了dht網路最核心的send和receive。下面只需要知道究竟是誰調用的,就徹底明晰了。好,這樣來看,這兩個函數對象都會傳送給rpc_manager,這個rcp_manager到底是怎樣一個神秘角色,dht_tracker和node_impl裡面的函數都歸它調用,讓我們來揭開它的神秘面紗吧。跟蹤到rpc_manager的建構函式,發現也只是些初始化動作,其中我們最關心的兩個函數對象分別被賦值給m_send和m_incoming。得,追蹤到這裡,已經到了絕路了。那麼,這兩個對象又是什麼時候調用的呢。雖然已經沒有明顯的線索,但是我們可以大致確定這兩個函數對象只能是rpc_manager來調用了,那就ctrl+f吧,來看看他們都會出現在哪些地方。
Rpc_manager::invoke(int message_id, udp::endpoint target_addr, shared_ptr<observer> o)
{
……
o->send(m);
……
m_send(m);
……
}
Rpc_manager::reply(msg& m, msg const& reply_to)
{
……
m_send(m);
……
}
void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
……
m_send(m);
……
}
哇,這麼多地方調用,看來不能以這種方式跟蹤了,我們得回退到client_test,接著往下讀了。
///////////////////////////////////////////////////////
首先是Rpc_manager::Invoke(),調用者如下:
get_peers_observer::reply()
find_data_observer::invoke()
announce_fun
refresh::invoke_pings_or_finish()
refresh::invoke()
find_data::invoke()
closest_nodes::invoke()