比特幣源碼解讀十四

上篇我們已經(jīng)進(jìn)入了比特幣的網(wǎng)絡(luò)節(jié)點(diǎn)部分,并且了解了本機(jī)節(jié)點(diǎn)是如何獲取自己的IP地址與外網(wǎng)地址的童擎,我們現(xiàn)在接著往下看:使用域種子(dnsseed)查找節(jié)點(diǎn)贪惹。

使用dnsseed指令 查找節(jié)點(diǎn)

我們先了解下域種子(dnsseed):苏章。IP地址是網(wǎng)絡(luò)上標(biāo)識站點(diǎn)的數(shù)字地址,為了方便記憶馍乙,采用域名來代替IP地址標(biāo)識站點(diǎn)地址布近。域名解析就是域名到IP地址的轉(zhuǎn)換過程。域名的解析工作由DNS服務(wù)器完成丝格。

這些DNS服務(wù)器提供比特幣節(jié)點(diǎn)的IP地址列表。 其中一些DNS種子提供了穩(wěn)定的比特幣偵聽節(jié)點(diǎn)的靜態(tài)IP地址列表棵譬。 一些DNS種子是BIND(Berkeley Internet Name Daemon)的自定義實(shí)現(xiàn)显蝌,它從搜索器或長時(shí)間運(yùn)行的比特幣節(jié)點(diǎn)收集的比特幣節(jié)點(diǎn)地址列表中返回一個(gè)隨機(jī)子集。 Bitcoin Core客戶端包含五種不同DNS種子的名稱订咸。 不同DNS種子的所有權(quán)和多樣性的多樣性為初始引導(dǎo)過程提供了高水平的可靠性曼尊。 在Bitcoin Core客戶端中,使用DNS種子的選項(xiàng)由選項(xiàng)switch -dnsseed控制(默認(rèn)設(shè)置為1)

然后我們看下 ThreadDNSAddressSeed 這個(gè)函數(shù)是如何使用域查找IP地址的脏嚷。

根據(jù)域名查找IP

下面骆撇,是通過對upnp端口的映射。

upnp端口映射

UPNP 的英文全稱是Universal Plug and Play父叙,即通用即插即用神郊。是各種各樣的智能設(shè)備、無線設(shè)備和個(gè)人電腦等實(shí)現(xiàn)遍布全球的對等網(wǎng)絡(luò)連接(P2P)的結(jié)構(gòu)趾唱。

比特幣客戶端是支持UPNP的涌乳,所以需要地址映射,我們現(xiàn)在就看下是如何映射的甜癞。要映射upnp設(shè)備是需要miniupnpc.lib這個(gè)庫夕晓,用于映射端口的API是UPNP_AddPortMapping這個(gè)接口。我們現(xiàn)在就看下源碼悠咱。

upnp端口映射

這部分我們了解了 UPNP的一些操作API蒸辆,關(guān)于更多這方面的API,大家可以自行搜索≌髁叮現(xiàn)在我們了解了如何發(fā)現(xiàn)其他節(jié)點(diǎn)和設(shè)備,現(xiàn)在我們就了解下和其他節(jié)點(diǎn)的消息通訊都做了哪些事情躬贡,這部分就是對各種消息的偵聽柒室。我們就先看下ThreadSocketHandler這個(gè)接口。我們和其他節(jié)點(diǎn)連通后逗宜,就需要進(jìn)行消息交換雄右,發(fā)送和接收數(shù)據(jù)就是通過這個(gè)接口來進(jìn)行的,我們現(xiàn)在就看下這個(gè)函數(shù)的內(nèi)部都做了些什么纺讲。這部分的源碼非常多擂仍,我們分幾個(gè)部分介紹:

1.在節(jié)點(diǎn)列表里查找已經(jīng)失去連接和未用的節(jié)點(diǎn)并刪除

刪除無用節(jié)點(diǎn)

2.查找哪個(gè)socket連接有數(shù)據(jù)接收

這里的源碼使用了文件描述符集fd_set(file descript set),既然其名字是一種集合,其實(shí)就是一long類型的數(shù)組熬甚,每一個(gè)數(shù)組元素都能與一打開的文件句柄(不管是socket句柄逢渔,還是其他文件或命名管道或設(shè)備句柄)建立聯(lián)系,建立聯(lián)系的工作由程序員完成乡括,當(dāng)調(diào)用select()時(shí)肃廓,由內(nèi)核根據(jù)IO狀態(tài)修改fd_set的內(nèi)容,由此來通知執(zhí)行了select()的進(jìn)程哪一socket或文件發(fā)生了可讀或可寫事件诲泌。

這里最重要的是select()函數(shù)盲赊,采用select()的方式原因是 :使用Select就可以完成非阻塞。(所謂非阻塞方式non-block敷扫,就是進(jìn)程或線程執(zhí)行此函數(shù)時(shí)不必非要等待事件的發(fā)生哀蘑,一旦執(zhí)行肯定返回,以返回值的不同來反映函數(shù)的執(zhí)行情況葵第,如果事件發(fā)生則與阻塞方式相同绘迁,若事件沒有發(fā)生則返回一個(gè)代碼來告知事件未發(fā)生,而進(jìn)程或線程繼續(xù)執(zhí)行卒密,所以效率較高)方式工作的程序缀台,它能夠監(jiān)視我們需要監(jiān)視的文件描述符的變化情況——讀寫或是異常。

Select的函數(shù)格式(我所說的是Unix系統(tǒng)下的伯克利socket編程 )我們看下其原型和參數(shù)哮奇。

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

maxfdp:是一個(gè)整數(shù)值膛腐,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1屏镊,不能錯(cuò)依疼!在Windows中這個(gè)參數(shù)的值無所謂,可以設(shè)置不正確而芥。

readfds:指向fd_set結(jié)構(gòu)的指針律罢,這個(gè)集合中應(yīng)該包括文件描述符,我們是要監(jiān)視這些文件描述符的讀變化的,即我們關(guān)心是否可以從這些文件中讀取數(shù)據(jù)了误辑,如果這個(gè)集合中有一個(gè)文件可讀沧踏,select就會返回一個(gè)大于0的值,表示有文件可讀巾钉,如果沒有可讀的文件翘狱,則根據(jù)timeout參數(shù)再判斷是否超時(shí),若超出timeout的時(shí)間砰苍,select返回0潦匈,若發(fā)生錯(cuò)誤返回負(fù)值∽迹可以傳入NULL值茬缩,表示不關(guān)心任何文件的讀變化

writefds:和readfds同理吼旧,只不過描述的是?是否可以從這些文件中寫數(shù)據(jù)了凰锡。

errorfds:用來監(jiān)聽讀寫文件或socket的異常。

timeout:用來設(shè)置超時(shí)時(shí)間圈暗。它可以使select處于三種狀態(tài)掂为,第一,若將NULL以形參傳入员串,即不傳入時(shí)間結(jié)構(gòu)勇哗,就是將select置于阻塞狀態(tài),一定等到監(jiān)視文件描述符集合中某個(gè)文件描述符發(fā)生變化為止昵济;第二智绸,若將時(shí)間值設(shè)為0秒0毫秒,就變成一個(gè)純粹的非阻塞函數(shù)访忿,不管文件描述符是否有變化,都立刻返回繼續(xù)執(zhí)行斯稳,文件無變化返回0海铆,有變化返回一個(gè)正值;第三挣惰,timeout的值大于0卧斟,這就是等待的超時(shí)時(shí)間,即select在timeout時(shí)間內(nèi)阻塞憎茂,超時(shí)時(shí)間之內(nèi)有事件到來就返回了珍语,否則在超時(shí)后不管怎樣一定返回,返回值同上述竖幔。

函數(shù)返回值:

負(fù)值:select錯(cuò)誤 正值:某些文件可讀寫或出錯(cuò) 0:等待超時(shí)板乙,沒有可讀寫或錯(cuò)誤的文件。

有了上面的認(rèn)識后,我們再看下源碼:

設(shè)置相應(yīng)的sokcet偵聽描述符

3.接收一個(gè)連接

因?yàn)槲覀兠總€(gè)節(jié)點(diǎn)既是其他節(jié)點(diǎn)的服務(wù)端募逞,也是其他節(jié)點(diǎn)的客戶端蛋铆,所以首先把我們當(dāng)服務(wù)器的時(shí)候,需要接收其他節(jié)點(diǎn)的連接放接,這就是我們要介紹的accept函數(shù)刺啦。

accept函數(shù)指定服務(wù)端去接受客戶端的連接,接收后纠脾,返回了客戶端套接字的標(biāo)識玛瘸,且獲得了客戶端套接字的“地方”(包括客戶端IP和端口信息等)。

接收其他節(jié)點(diǎn)的連接

4.從其他節(jié)點(diǎn)接收發(fā)送的數(shù)據(jù)

接收數(shù)據(jù)

socket接收從其他節(jié)點(diǎn)發(fā)送的數(shù)據(jù)使用的是recv()函數(shù)苟蹈。

(1)recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢糊渊,如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么recv函數(shù)返回SOCKET_ERROR汉操;

(2)如果s的發(fā)送緩沖中沒有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后再来,recv先檢查套接字s的接收緩沖區(qū),如果s接收緩沖區(qū)中沒有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù)磷瘤,那么recv就一直等待芒篷,直到協(xié)議把數(shù)據(jù)接收完畢。當(dāng)協(xié)議把數(shù)據(jù)接收完畢采缚,recv函數(shù)就把s的接收緩沖中的數(shù)據(jù)copy到buf中(注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長度针炉,所以在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完。recv函數(shù)僅僅是copy數(shù)據(jù)扳抽,真正的接收數(shù)據(jù)是協(xié)議來完成的)篡帕;

recv函數(shù)返回其實(shí)際copy的字節(jié)數(shù)。如果recv在copy時(shí)出錯(cuò)贸呢,那么它返回SOCKET_ERROR镰烧;如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,那么它返回0楞陷。

有了這個(gè)描述怔鳖,應(yīng)該就可以了解上面的代碼了。有了接收就要有發(fā)送固蛾,下面就是向其他節(jié)點(diǎn)通過socket?發(fā)送數(shù)據(jù)的代碼结执。

發(fā)送消息和連接狀態(tài)檢測

這里我們重要看下SocketSendData()函數(shù),看下是如何發(fā)送數(shù)據(jù)的艾凯。在看源碼前我們也介紹下socket的一個(gè)send()函數(shù)献幔,此函數(shù)就是向Socket另一方發(fā)送數(shù)據(jù)的函數(shù)()。函數(shù)原型為:

int send( SOCKET s, const char FAR *buf, int len, int flags );

該函數(shù)的第一個(gè)參數(shù)指定發(fā)送端套接字描述符趾诗;

第二個(gè)參數(shù)指明一個(gè)存放應(yīng)用程序要發(fā)送數(shù)據(jù)的緩沖區(qū)蜡感;

第三個(gè)參數(shù)指明實(shí)際要發(fā)送的數(shù)據(jù)的字節(jié)數(shù);

第四個(gè)參數(shù)為標(biāo)志位,一般情況下設(shè)置為MSG_NOSIGNAL(表示發(fā)送動作不愿被SIGPIPE信號中斷),同時(shí)還有其他標(biāo)志如:MSG_OOB(發(fā)送帶外數(shù)據(jù))MSG_DONTROUTE(告訴IP協(xié)議铸敏,目的主機(jī)在本地網(wǎng)絡(luò)缚忧,沒有必要查找路由表)MSG_DONTWAIT(設(shè)置為非阻塞操作)

發(fā)送數(shù)據(jù)

今天的內(nèi)容應(yīng)該是比較多了,我們了解了域種子杈笔,UPNP設(shè)備端口映射闪水,fd_set(文件描述集),Socket的通訊蒙具。這也是獲取其他節(jié)點(diǎn)的IP地址的目的球榆。也是網(wǎng)絡(luò)中進(jìn)行通訊的常用方式。這篇就寫到這里了禁筏。大家可以在網(wǎng)絡(luò)中搜相關(guān)內(nèi)容進(jìn)行詳細(xì)了解持钉。




作者:區(qū)塊鏈研習(xí)社比特幣源碼研讀班,black

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篱昔,一起剝皮案震驚了整個(gè)濱河市每强,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌州刽,老刑警劉巖空执,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異穗椅,居然都是意外死亡辨绊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門匹表,熙熙樓的掌柜王于貴愁眉苦臉地迎上來门坷,“玉大人,你說我怎么就攤上這事袍镀∧觯” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵苇羡,是天一觀的道長敏簿。 經(jīng)常有香客問我,道長宣虾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任温数,我火速辦了婚禮绣硝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撑刺。我一直安慰自己鹉胖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甫菠,像睡著了一般挠铲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寂诱,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天拂苹,我揣著相機(jī)與錄音,去河邊找鬼痰洒。 笑死瓢棒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丘喻。 我是一名探鬼主播脯宿,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泉粉!你這毒婦竟也來了连霉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嗡靡,失蹤者是張志新(化名)和其女友劉穎跺撼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叽躯,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡财边,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了点骑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酣难。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖黑滴,靈堂內(nèi)的尸體忽然破棺而出憨募,到底是詐尸還是另有隱情,我是刑警寧澤袁辈,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布菜谣,位于F島的核電站,受9級特大地震影響晚缩,放射性物質(zhì)發(fā)生泄漏尾膊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一荞彼、第九天 我趴在偏房一處隱蔽的房頂上張望冈敛。 院中可真熱鬧,春花似錦鸣皂、人聲如沸抓谴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽癌压。三九已至仰泻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滩届,已是汗流浹背集侯。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丐吓,地道東北人浅悉。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像券犁,于是被迫代替她去往敵國和親术健。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容