日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

java dht 爬虫_P2P中DHT网络爬虫

發(fā)布時(shí)間:2024/8/1 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java dht 爬虫_P2P中DHT网络爬虫 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

DHT網(wǎng)絡(luò)爬蟲(chóng)基于DHT網(wǎng)絡(luò)構(gòu)建了一個(gè)P2P資源搜索引擎。這個(gè)搜索引擎不但可以用于構(gòu)建DHT網(wǎng)絡(luò)中活躍的資源索引(活躍的資源意味著該網(wǎng)絡(luò)中肯定有人至少持有該資源的部分?jǐn)?shù)據(jù)),還可以分析出該網(wǎng)絡(luò)中的熱門(mén)分享資源。小蝦不久前發(fā)布了一個(gè)這樣的搜索引擎:磁力搜索。他也寫(xiě)博客對(duì)此稍作了介紹:寫(xiě)了個(gè)磁力搜索的網(wǎng)頁(yè) - 收錄最近熱門(mén)分享的資源。網(wǎng)絡(luò)上其實(shí)也有其他人做了類(lèi)似的應(yīng)用:DHT monitoring,Crawling Bittorrent DHT

但是他的這篇文章僅介紹了DHT網(wǎng)絡(luò)的大致工作原理,并且這個(gè)爬蟲(chóng)的具體工作原理也沒(méi)有提到。對(duì)此我查閱了些文章及代碼,雖然從原理上梳理出了整個(gè)實(shí)現(xiàn)方案,但很多細(xì)節(jié)還是不甚清楚。所以本文僅作概要介紹。

DHT/Magnet/Torrent

在P2P網(wǎng)絡(luò)中,要通過(guò)種子文件下載一個(gè)資源,需要知道整個(gè)P2P網(wǎng)絡(luò)中有哪些計(jì)算機(jī)正在下載/上傳該資源。這里將這些提供某個(gè)資源下載的計(jì)算機(jī)定義為peer。傳統(tǒng)的P2P網(wǎng)絡(luò)中,存在一些tracker服務(wù)器,這些服務(wù)器的作用主要用于跟蹤某個(gè)資源有哪些關(guān)聯(lián)的peer。下載這個(gè)資源當(dāng)然得首先取得這些peer。

DHT的出現(xiàn)用于解決當(dāng)tracker服務(wù)器不可用時(shí),P2P客戶(hù)端依然可以取得某個(gè)資源的peer。DHT解決這個(gè)問(wèn)題,是因?yàn)樗鼘⒃瓉?lái)tracker上的資源peer信息分散到了整個(gè)網(wǎng)絡(luò)中。這里將實(shí)現(xiàn)了DHT協(xié)議的計(jì)算機(jī)定義為節(jié)點(diǎn)(node)。通常一個(gè)P2P客戶(hù)端程序既是peer也是節(jié)點(diǎn)。DHT網(wǎng)絡(luò)有多種實(shí)現(xiàn)算法,例如Kademlia。

當(dāng)某個(gè)P2P客戶(hù)端通過(guò)種子文件下載資源時(shí),如果沒(méi)有tracker服務(wù)器,它就會(huì)向DHT網(wǎng)絡(luò)查詢(xún)這個(gè)資源對(duì)應(yīng)的peer列表。資源的標(biāo)識(shí)在DHT網(wǎng)絡(luò)中稱(chēng)為infohash,是一個(gè)20字節(jié)長(zhǎng)的字符串,一般通過(guò)sha1算法獲得,也就是一個(gè)類(lèi)似UUID的東西。

實(shí)際上,種子文件本身就對(duì)應(yīng)著一個(gè)infohash,這個(gè)infohash是通過(guò)種子文件的文件描述信息動(dòng)態(tài)計(jì)算得到。一個(gè)種子文件包含了對(duì)應(yīng)資源的描述信息,例如文件名、文件大小等。Magnet,這里指的是磁力鏈接,它是一個(gè)類(lèi)似URL的字符串地址。P2P軟件通過(guò)磁力鏈接,會(huì)下載到一個(gè)種子文件,然后根據(jù)該種子文件繼續(xù)真實(shí)資源的下載。

磁力鏈接中包含的最重要的信息就是infohash。這個(gè)infohash一般為40字節(jié)或32字節(jié),它其實(shí)只是資源infohash(20字節(jié))的一種編碼形式。

Kademlia

Kademlia是DHT網(wǎng)絡(luò)的一種實(shí)現(xiàn)。網(wǎng)絡(luò)上關(guān)于這個(gè)算法的文章,主要是圍繞整個(gè)DHT網(wǎng)絡(luò)的實(shí)現(xiàn)原理進(jìn)行論述。個(gè)人覺(jué)得這些文章很蛋疼,基本上讀了之后對(duì)于要如何去實(shí)現(xiàn)一個(gè)DHT客戶(hù)端還是沒(méi)有概念。這里主要可參考P2P中DHT網(wǎng)絡(luò)介紹,以及BitTorrent網(wǎng)站上的DHT協(xié)議描述

Kad的主要目的是用于查詢(xún)某個(gè)資源對(duì)應(yīng)的peer列表,而這個(gè)peer列表實(shí)際上是分散在整個(gè)網(wǎng)絡(luò)中。網(wǎng)絡(luò)中節(jié)點(diǎn)數(shù)量很大,如果要獲得peer列表,最簡(jiǎn)單的做法無(wú)非就是依次詢(xún)問(wèn)網(wǎng)絡(luò)中的每個(gè)節(jié)點(diǎn)。這當(dāng)然不可行。所以在Kad算法中,設(shè)立了一個(gè)路由表。每一個(gè)節(jié)點(diǎn)都有一份路由表。這個(gè)是按照節(jié)點(diǎn)之間的距離關(guān)系構(gòu)建出來(lái)的。節(jié)點(diǎn)之間的距離當(dāng)然也有特定的算法定義,在Kad中通過(guò)對(duì)兩個(gè)節(jié)點(diǎn)的ID進(jìn)行異或操作得到。節(jié)點(diǎn)的ID和infohash通過(guò)相同算法構(gòu)建,都是20字節(jié)長(zhǎng)度。節(jié)點(diǎn)和infohash之間也有距離關(guān)系,實(shí)際上表示的是節(jié)點(diǎn)和資源的距離關(guān)系。

有了這個(gè)路由表之后,再通過(guò)一個(gè)基于距離關(guān)系的查找算法,就可以實(shí)現(xiàn)不用挨個(gè)遍歷就找到特定的節(jié)點(diǎn)。而查找資源peer這個(gè)操作,正是基于節(jié)點(diǎn)查找這個(gè)過(guò)程。

路由表的實(shí)現(xiàn),按我的理解,有點(diǎn)類(lèi)似一般的hash表結(jié)構(gòu)。在這個(gè)表中有160個(gè)桶,稱(chēng)為K桶,這個(gè)桶的數(shù)量在實(shí)現(xiàn)上可以動(dòng)態(tài)增長(zhǎng)。每個(gè)桶保存有限個(gè)元素,例如K取值為8,指的就是這個(gè)桶最多保存8個(gè)元素。每個(gè)元素就是一個(gè)節(jié)點(diǎn),節(jié)點(diǎn)包含節(jié)點(diǎn)ID、地址信息以及peer信息。這些桶可以通過(guò)距離值索引得到,即距離值會(huì)經(jīng)過(guò)一個(gè)hash算法,使其值落到桶的索引范圍內(nèi)。

要加入一個(gè)DHT網(wǎng)絡(luò),需要首先知道這個(gè)網(wǎng)絡(luò)中的任意一個(gè)節(jié)點(diǎn)。如何獲得這個(gè)節(jié)點(diǎn)?在一些開(kāi)源的P2P軟件中,會(huì)提供一些節(jié)點(diǎn)地址,例如transmission中提供的dht.transmissionbt.com:6881。

協(xié)議

Kad定義了節(jié)點(diǎn)之間的交互協(xié)議。這些協(xié)議支撐了整個(gè)DHT網(wǎng)絡(luò)里信息分布式存儲(chǔ)的實(shí)現(xiàn)。這些協(xié)議都是使用UDP來(lái)傳送。其協(xié)議格式使用一種稱(chēng)為bencode的編碼方式來(lái)編碼協(xié)議數(shù)據(jù)。bencode是一種文本格式的編碼,它還用于種子文件內(nèi)的信息編碼。

Kad協(xié)議具體格式可參考BitTorrent的定義:DHT Protocol。這些協(xié)議包括4種請(qǐng)求:ping,find_node,get_peer,announce_peer。在有些文檔中這些請(qǐng)求的名字會(huì)有不同,例如announce_peer又被稱(chēng)為store,get_peer被稱(chēng)為find_value。這4種請(qǐng)求中,都會(huì)有對(duì)應(yīng)的回應(yīng)消息。其中最重要的消息是get_peer,其目的在于在網(wǎng)絡(luò)中查找某個(gè)資源對(duì)應(yīng)的peer列表。

值得一提的是,所有這些請(qǐng)求,包括各種回應(yīng),都可以用于處理該消息的節(jié)點(diǎn)構(gòu)建路由表。因?yàn)槁酚杀肀举|(zhì)就是存儲(chǔ)網(wǎng)絡(luò)中的節(jié)點(diǎn)信息。

ping

用于確定某個(gè)節(jié)點(diǎn)是否在線(xiàn)。這個(gè)請(qǐng)求主要用于輔助路由表的更新。

find_node

用于查找某個(gè)節(jié)點(diǎn),以獲得其地址信息。當(dāng)某個(gè)節(jié)點(diǎn)接收到該請(qǐng)求后,如果目標(biāo)節(jié)點(diǎn)不在自己的路由表里,那么就返回離目標(biāo)節(jié)點(diǎn)較近的K個(gè)節(jié)點(diǎn)。這個(gè)消息可用于節(jié)點(diǎn)啟動(dòng)時(shí)構(gòu)建路由表。通過(guò)find_node方式構(gòu)建路由表,其實(shí)現(xiàn)方式為向DHT網(wǎng)絡(luò)查詢(xún)自己。那么,接收該查詢(xún)的節(jié)點(diǎn)就會(huì)一直返回其他節(jié)點(diǎn)了列表,查詢(xún)者遞歸查詢(xún),直到無(wú)法查詢(xún)?yōu)橹埂D敲?#xff0c;什么時(shí)候無(wú)法繼續(xù)查詢(xún)呢?這一點(diǎn)我也不太清楚。每一次查詢(xún)得到的都是離目標(biāo)節(jié)點(diǎn)更接近的節(jié)點(diǎn)集,那么理論上經(jīng)過(guò)若干次遞歸查詢(xún)后,就無(wú)法找到離目標(biāo)節(jié)點(diǎn)更近的節(jié)點(diǎn)了,因?yàn)樽罱墓?jié)點(diǎn)是自己,但自己還未完全加入網(wǎng)絡(luò)。這意味著最后所有節(jié)點(diǎn)都會(huì)返回空的節(jié)點(diǎn)集合,這樣就算查詢(xún)結(jié)束?

實(shí)際上,通過(guò)find_node來(lái)構(gòu)建路由表,以及順帶加入DHT網(wǎng)絡(luò),這種方式什么時(shí)候停止在我看來(lái)并不重要。路由表的構(gòu)建并不需要在啟動(dòng)時(shí)構(gòu)建完成,在以后與其他節(jié)點(diǎn)的交互過(guò)程中,路由表本身就會(huì)慢慢地得到構(gòu)建。在初始階段盡可能地通過(guò)find_node去與其他節(jié)點(diǎn)交互,最大的好處無(wú)非就是盡早地讓網(wǎng)絡(luò)中的其他節(jié)點(diǎn)認(rèn)識(shí)自己。

get_peer

通過(guò)資源的infohash獲得資源對(duì)應(yīng)的peer列表。當(dāng)查詢(xún)者獲得資源的peer列表后,它就可以通過(guò)這些peer進(jìn)行資源下載了。收到該請(qǐng)求的節(jié)點(diǎn)會(huì)在自己的路由表中查找該infohash,如果有收錄,就返回對(duì)應(yīng)的peer列表。如果沒(méi)有,則返回離該infohash較近的若干個(gè)節(jié)點(diǎn)。查詢(xún)者若收到的是節(jié)點(diǎn)列表,那么就會(huì)遞歸查找。這個(gè)過(guò)程同find_node一樣。

值得注意的是,get_peer的回應(yīng)消息里會(huì)攜帶一個(gè)token,該token會(huì)用于稍后的announce_peer請(qǐng)求。

announce_peer

該請(qǐng)求主要目的在于通知,通知其他節(jié)點(diǎn)自己開(kāi)始下載某個(gè)資源。這個(gè)消息用于構(gòu)建網(wǎng)絡(luò)中資源的peer列表。當(dāng)一個(gè)已經(jīng)加入DHT網(wǎng)絡(luò)的P2P客戶(hù)端通過(guò)種子文件開(kāi)始下載資源時(shí),首先在網(wǎng)絡(luò)中查詢(xún)?cè)撡Y源的peer列表,這個(gè)過(guò)程通過(guò)get_peer完成。當(dāng)某個(gè)節(jié)點(diǎn)從get_peer返回peer時(shí),查詢(xún)者開(kāi)始下載,然后通過(guò)announce_peer告訴返回這個(gè)peer的節(jié)點(diǎn)。

announce_peer中會(huì)攜帶get_peer回應(yīng)消息里的token。關(guān)于這一點(diǎn),我有一個(gè)疑問(wèn)是,在P2P中DHT網(wǎng)絡(luò)介紹文檔中提到:

(announce_peer)同時(shí)會(huì)把自己的peer信息發(fā)送給先前的告訴者和自己K桶中的k個(gè)最近的節(jié)點(diǎn)存儲(chǔ)該peer-list信息

不管這里提到的K的最近的節(jié)點(diǎn)是離自己最近,還是離資源infohash最近的節(jié)點(diǎn),因?yàn)樘幚韆nnounce_peer消息時(shí),有一個(gè)token的驗(yàn)證過(guò)程。但是這K個(gè)節(jié)點(diǎn)中,并沒(méi)有在之前創(chuàng)建對(duì)應(yīng)的token。我通過(guò)transmission中的DHT實(shí)現(xiàn)做了個(gè)數(shù)據(jù)收集,可以證明的是,announce_peer消息是不僅僅會(huì)發(fā)給get_peer的回應(yīng)者的。

DHT爬蟲(chóng)

DHT爬蟲(chóng)是一個(gè)遵循Kad協(xié)議的假節(jié)點(diǎn)程序。具體可以參考小蝦發(fā)布的那個(gè)網(wǎng)站應(yīng)用:磁力搜索。

這個(gè)爬蟲(chóng)的實(shí)現(xiàn)方式,主要包含以下內(nèi)容:

通過(guò)其他節(jié)點(diǎn)的announce_peer發(fā)來(lái)的infohash確認(rèn)網(wǎng)絡(luò)中有某個(gè)資源可被下載

通過(guò)從網(wǎng)絡(luò)中獲取這個(gè)資源的種子文件,來(lái)獲得該資源的描述

通過(guò)累計(jì)收集得到的資源信息,就可以提供一個(gè)資源搜索引擎,或者構(gòu)建資源統(tǒng)計(jì)信息。以下進(jìn)一步描述實(shí)現(xiàn)細(xì)節(jié)。整個(gè)爬蟲(chóng)的實(shí)現(xiàn)依賴(lài)了一個(gè)很重要的信息,那就是資源的infohash實(shí)際上就是一個(gè)磁力鏈接(當(dāng)然需要包裝一下數(shù)據(jù))。這意味著一旦我們獲得了一個(gè)infohash,我們就等于獲得了一個(gè)種子。

獲得資源通知

當(dāng)爬蟲(chóng)程序加入DHT網(wǎng)絡(luò)后,它總會(huì)收到其他節(jié)點(diǎn)發(fā)來(lái)的announce_peer消息。announce_peer消息與get_peer消息里都帶了資源的infohash,但是get_peer里的infohash對(duì)應(yīng)的資源在該網(wǎng)絡(luò)中不一定存在,即該資源沒(méi)有任何可用peer。而announce_peer則表示已經(jīng)確認(rèn)了該網(wǎng)絡(luò)中有節(jié)點(diǎn)正在下載該資源,也即該資源的數(shù)據(jù)確實(shí)存在該網(wǎng)絡(luò)中。

所以,爬蟲(chóng)程序需要盡最大努力地獲取其他節(jié)點(diǎn)發(fā)來(lái)的announce_peer消息。如果announce_peer消息會(huì)發(fā)送給離消息發(fā)送節(jié)點(diǎn)較近的節(jié)點(diǎn),那么,一方面,爬蟲(chóng)程序應(yīng)該將自己告訴網(wǎng)絡(luò)中盡可能多的節(jié)點(diǎn)。這可以通過(guò)一次完整的find_node操作實(shí)現(xiàn)。另一方面,爬蟲(chóng)程序內(nèi)部實(shí)現(xiàn)可以部署多個(gè)DHT節(jié)點(diǎn),總之目的在于盡可能地讓爬蟲(chóng)程序稱(chēng)為其他節(jié)點(diǎn)的較近者。

當(dāng)收集到infohash之后,爬蟲(chóng)程序還需要通過(guò)該infohash獲得對(duì)應(yīng)資源的描述信息。

獲取資源信息

獲得資源描述信息,其實(shí)就是通過(guò)infohash獲得對(duì)應(yīng)的種子文件。這需要實(shí)現(xiàn)P2P協(xié)議里的文件分享協(xié)議。種子文件的獲取其實(shí)就是一個(gè)文件下載過(guò)程,下載到種子文件之后,就可以獲取到資源描述。這個(gè)過(guò)程一種簡(jiǎn)單的方法,就是從infohash構(gòu)建出一個(gè)磁力鏈接,然后交給一個(gè)支持磁力下載的程序下載種子。

從infohash構(gòu)建出磁力鏈接非常簡(jiǎn)單,只需要將infohash編碼成磁力鏈接的xt字段即可,構(gòu)建實(shí)現(xiàn)可以從transmission源碼里找到:

/* 這個(gè)算法其實(shí)和printf("%02x", sha1[i])一樣 */

void tr_sha1_to_hex (char *out, const unsigned char *sha1)

{

int i;

static const char hex[] = "0123456789abcdef";

for (i=0; i<20; ++i) {

const unsigned int val = *sha1++;

*out++ = hex[val >> 4];

*out++ = hex[val & 0xf];

}

*out = '\0';

}

void appendMagnet(FILE *fp, const unsigned char *info_hash) {

char out[48];

tr_sha1_to_hex(out, info_hash);

fprintf(fp, "magnet:?xt=urn:btih:%s", out);

}

現(xiàn)在你就可以做一個(gè)實(shí)驗(yàn),在transmission的DHT實(shí)現(xiàn)中,在announce_peer消息的處理代碼中,將收到的infohash通過(guò)上面的appendMagnet轉(zhuǎn)換為磁力鏈接輸出到日志文件里。然后,可以通過(guò)支持磁力鏈接的程序(例如QQ旋風(fēng))直接下載。有趣的是,當(dāng)QQ旋風(fēng)開(kāi)始下載該磁力鏈接對(duì)應(yīng)的種子文件時(shí),你自己的測(cè)試程序能收到QQ旋風(fēng)程序發(fā)出的announce_peer消息。當(dāng)然,你得想辦法讓這個(gè)測(cè)試程序盡可能地讓其他節(jié)點(diǎn)知道你,這可以通過(guò)很多方式實(shí)現(xiàn)。

總結(jié)

最終的DHT爬蟲(chóng)除了需要實(shí)現(xiàn)DHT協(xié)議之外,還需要實(shí)現(xiàn)P2P文件下載協(xié)議,甚至包括一個(gè)種子文件解析模塊。看起來(lái)包含不小的工作量。而如果要包裝為一個(gè)資源搜索引擎,還會(huì)涉及到資源存儲(chǔ)以及搜索,更別說(shuō)前端呈現(xiàn)了。這個(gè)時(shí)候,如果你使用的語(yǔ)言不包含這些工具庫(kù),那實(shí)在是太悲劇了。沒(méi)錯(cuò),我就沒(méi)找到一個(gè)erlang DHT庫(kù)(倒是有erlang實(shí)現(xiàn)的BT客戶(hù)端,懶得去剝了)。

UPDATE

通過(guò)詳細(xì)閱讀transmission里的DHT實(shí)現(xiàn),一些之前的疑惑隨之解開(kāi)。

announce_peer會(huì)發(fā)給哪些節(jié)點(diǎn)

在一次對(duì)infohash的查詢(xún)過(guò)程中,所有對(duì)本節(jié)點(diǎn)發(fā)出的get_peer作出回應(yīng)的節(jié)點(diǎn)(不論這個(gè)回應(yīng)節(jié)點(diǎn)回應(yīng)的是nodes還是peers),當(dāng)本節(jié)點(diǎn)取得peer信息時(shí),就會(huì)對(duì)所有這些節(jié)點(diǎn)發(fā)出announce_peer。get_peer的回應(yīng)消息里,不論是peer還是node,都會(huì)攜帶一個(gè)token,這樣在將來(lái)收到對(duì)方的announce_peer時(shí),就可以驗(yàn)證該token。

節(jié)點(diǎn)和bucket狀態(tài)

在本地的路由表中,保存的node是有狀態(tài)之分的。狀態(tài)分為三種:good/dubious/bad。good節(jié)點(diǎn)基本可以斷定該節(jié)點(diǎn)是一個(gè)在線(xiàn)的并且可以正常回應(yīng)消息的節(jié)點(diǎn);而bad節(jié)點(diǎn)則是可確定的無(wú)效節(jié)點(diǎn),通常會(huì)盡快從路由表中移除;而dubious則是介于good和bad節(jié)點(diǎn)之間,表示可能有問(wèn)題的節(jié)點(diǎn),需要進(jìn)一步發(fā)送例如ping消息來(lái)確認(rèn)其狀態(tài)。路由表中應(yīng)該盡可能保證保存的是good節(jié)點(diǎn),對(duì)查詢(xún)消息的回應(yīng)里也需攜帶好的節(jié)點(diǎn)。

bucket也是有狀態(tài)的,當(dāng)一個(gè)bucket中的所有節(jié)點(diǎn)在一定時(shí)間之內(nèi)都沒(méi)有任何活動(dòng)的時(shí)候,該bucket則應(yīng)該考慮進(jìn)行狀態(tài)的確認(rèn),確認(rèn)方式可以隨機(jī)選擇該bucket中的節(jié)點(diǎn)進(jìn)行find_node操作(這也是find_node除了用于啟動(dòng)之外的唯一作用,但具體實(shí)現(xiàn)不見(jiàn)得使用這種方式)。沒(méi)有消息來(lái)往的bucket則應(yīng)該考慮移除。DHT中幾乎所有操作都會(huì)涉及到bucket的索引,如果索引到一個(gè)所有節(jié)點(diǎn)都有問(wèn)題的bucket,那么該操作可能就無(wú)法完成。

search在何時(shí)停止

首先,某次發(fā)起的search,無(wú)論是對(duì)node還是對(duì)peer,都可能導(dǎo)致進(jìn)一步產(chǎn)生若干個(gè)search。這些search都是基于transaction id來(lái)標(biāo)識(shí)的。由一次search導(dǎo)致產(chǎn)生的所有子search都擁有相同的transaction id,以使得在該search成功或失敗時(shí)可以通過(guò)該transaction id來(lái)刪除對(duì)應(yīng)的所有search。transaction id也就是DHT中每個(gè)消息消息頭”t”的值。

但是search何時(shí)停止?transmission中是通過(guò)超時(shí)機(jī)制來(lái)停止。在search過(guò)程中,如果長(zhǎng)時(shí)間沒(méi)有收到跟該search關(guān)聯(lián)的節(jié)點(diǎn)發(fā)來(lái)的回應(yīng)消息,那么就撤銷(xiāo)該search,表示搜索失敗。

參考資料

總結(jié)

以上是生活随笔為你收集整理的java dht 爬虫_P2P中DHT网络爬虫的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。