1.ACE反應(yīng)器框架簡介
反應(yīng)器(Reactor):用于事件多路分離和分派的體系結(jié)構(gòu)模式
通常的,對一個文件描述符指定的文件或設(shè)備, 有兩種工作方式:?阻塞與非阻塞。所謂阻塞方式的意思是指, 當(dāng)試圖對該文件描述符進(jìn)行讀寫時,如果當(dāng)時沒有東西可讀,或者暫時不可寫, 程序就進(jìn)入等待狀態(tài), 直到有東西可讀或者可寫為止。而對于非阻塞狀態(tài),如果沒有東西可讀, 或者不可寫, 讀寫函數(shù)馬上返回,而不會等待。
在前面的章節(jié)中提到的Tcp通信的例子中,就是采用的阻塞式的工作方式:當(dāng)接收tcp數(shù)據(jù)時,如果遠(yuǎn)端沒有數(shù)據(jù)可以讀,則會一直阻塞到讀到需要的數(shù)據(jù)為止。這種方式的傳輸和傳統(tǒng)的被動方法的調(diào)用類似,非常直觀,并且簡單有效,但是同樣也存在一個效率問題,如果你是開發(fā)一個面對著數(shù)千個連接的服務(wù)器程序,對每一個客戶端都采用阻塞的方式通信,如果存在某個非常耗時的讀寫操作時,其它的客戶端通信將無法響應(yīng),效率非常低下。
一種常用做法是:每建立一個Socket連接時,同時創(chuàng)建一個新線程對該Socket進(jìn)行單獨(dú)通信(采用阻塞的方式通信)。這種方式具有很高的響應(yīng)速度,并且控制起來也很簡單,在連接數(shù)較少的時候非常有效,但是如果對每一個連接都產(chǎn)生一個線程的無疑是對系統(tǒng)資源的一種浪費(fèi),如果連接數(shù)較多將會出現(xiàn)資源不足的情況。
另一種較高效的做法是:服務(wù)器端保存一個Socket連接列表,然后對這個列表進(jìn)行輪詢,如果發(fā)現(xiàn)某個Socket端口上有數(shù)據(jù)可讀時(讀就緒),則調(diào)用該socket連接的相應(yīng)讀操作;如果發(fā)現(xiàn)某個Socket端口上有數(shù)據(jù)可寫時(寫就緒),則調(diào)用該socket連接的相應(yīng)寫操作;如果某個端口的Socket連接已經(jīng)中斷,則調(diào)用相應(yīng)的析構(gòu)方法關(guān)閉該端口。這樣能充分利用服務(wù)器資源,效率得到了很大提高。
在Socket編程中就可以通過select等相關(guān)API實(shí)現(xiàn)這一方式。但直接用這些API控制起來比較麻煩,并且也難以控制和移植,在ACE中可以通過Reactor模式簡化這一開發(fā)過程。
反應(yīng)器本質(zhì)上提供一組更高級的編程抽象,簡化了事件驅(qū)動的分布式應(yīng)用的設(shè)計(jì)和實(shí)現(xiàn)。除此而外,反應(yīng)器還將若干不同種類的事件的多路分離集成到易于使用的API中。特別地,反應(yīng)器對基于定時器的事件、信號事件、基于I/O端口監(jiān)控的事件和用戶定義的通知進(jìn)行統(tǒng)一地處理。
ACE中的反應(yīng)器與若干內(nèi)部和外部組件協(xié)同工作。其基本概念是反應(yīng)器框架檢測事件的發(fā)生(通過在OS事件多路分離接口上進(jìn)行偵聽),并發(fā)出對預(yù)登記事件處理器(eventhandler)對象中的方法的"回調(diào)"(callback)。該方法由應(yīng)用開發(fā)者實(shí)現(xiàn),其中含有應(yīng)用處理此事件的特定代碼。
使用ACE的反應(yīng)器,只需如下幾步:
創(chuàng)建事件處理器,以處理他所感興趣的某事件。 在反應(yīng)器上登記,通知說他有興趣處理某事件,同時傳遞他想要用以處理此事件的事件處理器的指針給反應(yīng)器。 隨后反應(yīng)器框架將自動地:
在內(nèi)部維護(hù)一些表,將不同的事件類型與事件處理器對象關(guān)聯(lián)起來。 在用戶已登記的某個事件發(fā)生時,反應(yīng)器發(fā)出對處理器中相應(yīng)方法的回調(diào)。 反應(yīng)器模式在ACE中被實(shí)現(xiàn)為ACE_Reactor類,它提供反應(yīng)器框架的功能接口。
如上面所提到的,反應(yīng)器將事件處理器對象作為服務(wù)提供者使用。反應(yīng)器內(nèi)部記錄某個事件處理器的特定事件的相關(guān)回調(diào)方法。當(dāng)這些事件發(fā)生時,反應(yīng)器會創(chuàng)建這種事件和相應(yīng)的事件處理器的關(guān)聯(lián)。
事件處理器
事件處理器就是需要通過輪詢發(fā)生事件改變的對象列表中的對象,如在上面的例子中就是連接的客戶端,每個客戶端都可以看成一個事件處理器。 回調(diào)事件
就是反應(yīng)器支持的事件,如Socket讀就緒,寫就緒。拿上面的例子來說,如果某個客戶端(事件處理器)在反應(yīng)器中注冊了讀就緒事件,當(dāng)客戶端給服務(wù)器發(fā)送一條消息的時候,就會觸發(fā)這個客戶端的數(shù)據(jù)可讀的回調(diào)函數(shù)。 在反應(yīng)器框架中,所有應(yīng)用特有的事件處理器都必須由ACE_Event_Handler的抽象接口類派生。可以通過重載相應(yīng)的"handle_"方法實(shí)現(xiàn)相關(guān)的回調(diào)方法。
使用ACE_Reactor基本上有三個步驟:
創(chuàng)建ACE_Event_Handler的子類,并在其中實(shí)現(xiàn)適當(dāng)?shù)?#34;handle_"方法,以處理你想要此事件處理器為之服務(wù)的事件類型。 通過調(diào)用反應(yīng)器對象的register_handler(),將你的事件處理器登記到反應(yīng)器。 在事件發(fā)生時,反應(yīng)器將自動回調(diào)相應(yīng)的事件處理器對象的適當(dāng)?shù)膆andle_"方法。 下面我就以一個Socket客戶端的例子為例簡單的說明反應(yīng)器的基本用法。
?
Cpp代碼??
#include?<ace/OS.h>?? #include?<ace/Reactor.h>?? #include?<ace/SOCK_Connector.h>??? ?? #include?<string>?? #include?<iostream>?? using?namespace?std;?? ?? class?MyClient:public?ACE_Event_Handler??? {?? public:?? ????bool?open()?? ????{?? ????????ACE_SOCK_Connector?connector;?? ????????ACE_INET_Addr?addr(3000,"127.0.0.1");?? ????????ACE_Time_Value?timeout(5,0);?? ????????if(connector.connect(peer,addr,&timeout)?!=?0)?? ????????{?? ????????????cout<<endl<<"connecetd?fail";?? ????????????return?false;?? ????????}?? ????????ACE_Reactor::instance()->register_handler(this,ACE_Event_Handler::READ_MASK);?? ????????cout<<endl<<"connecetd?";?? ????????return?true;?? ????}?? ?? ????ACE_HANDLE?get_handle(void)?const?? ????{?? ????????return?peer.get_handle();?? ????}?? ?? ????int?handle_input?(ACE_HANDLE?fd)?? ????{?? ????????int?rev=0;?? ????????ACE_Time_Value?timeout(5,0);?? ????????if((rev=peer.recv(buffer,1000,&timeout))>0)?? ????????{?? ????????????buffer[rev]='\0';?? ????????????cout<<endl<<"rev:\t"<<buffer<<endl;?? ????????}?? ????????return?3;?? ????}?? ?? private:?? ????ACE_SOCK_Stream?peer;?? ????char?buffer[1024];?? };?? ?? int?main(int?argc,?char?*argv[])??? {?? ????MyClient?client;?? ????client.open();?? ?? ????while(true)?? ????{?? ????????ACE_Reactor::instance()->handle_events();??? ????}?? ?? ????return?0;??? }?? ? ?
在這個例子中,客戶端連接上服務(wù)器后,通過ACE_Reactor::instance()->register_handler(this,ACE_Event_Handler::READ_MASK)注冊了一個讀就緒的回調(diào)函數(shù),當(dāng)服務(wù)器端給客戶端發(fā)消息的時候,會自動觸發(fā)handle_input()函數(shù),將接收到的信息打印出來。
這個例子只是為了演示反應(yīng)器的基本用法,并不完善,我將在下一節(jié)中對如何在Socket通信中使用反應(yīng)器做進(jìn)一步的介紹。
?
在Socket編程中,常見的事件就是"讀就緒","寫就緒",通過對這兩個事件的捕獲分發(fā),可以實(shí)現(xiàn)Socket中的異步操作。
Socket編程中的事件處理器
在前面我們已經(jīng)介紹過,在ACE反應(yīng)器框架中,任何都必須派生自ACE_Event_Handler類,并通過重載其相應(yīng)會調(diào)事件處理函數(shù)來實(shí)現(xiàn)相應(yīng)的回調(diào)處理的。在Socket編程中,我們通常需要重載的函數(shù)有
handle_input()
當(dāng)I/O句柄(比如UNIX中的文件描述符)上的輸入可用時,反應(yīng)器自動回調(diào)該方法。 handle_output()
當(dāng)I/O設(shè)備的輸出隊(duì)列有可用空間時,反應(yīng)器自動回調(diào)該方法。 handle_close()
當(dāng)事件處理器中的事件從Reactor中移除的時候調(diào)用。 此外,為了使Reactor能通過I/O句柄找到對應(yīng)的事件處理器,還必須重載其get_handle()方法以使得Reactor建立起I/O句柄和事件處理器的關(guān)聯(lián)。
使用Reactor框架。
下面我們將以一個客戶端的程序?yàn)槔?#xff0c;介紹如何在Socket編程中使用Reactor框架。
一.建立一個客戶端對象(事件處理器)。
客戶端對象就是一個事件處理器,其聲明如下:
?
Cpp代碼??
class?Client:public?ACE_Event_Handler?? {?? public:?? ????ACE_HANDLE?get_handle(void)?const;?? ????int?handle_input?(ACE_HANDLE?fd);?? ????int?handle_close?(ACE_HANDLE?handle,?? ACE_Reactor_Mask?close_mask);?? ????ACE_SOCK_Stream&?Peer();?? private:?? ????ACE_SOCK_Stream?peer;?? };?? ? ?
在Client端中我只關(guān)心"讀就緒"事件,故只重載了handle_input函數(shù)(大多數(shù)應(yīng)用下只需要重載handle_input函數(shù))。另外,在客戶端還保存了一個ACE_SOCK_Stream的peer對象用來進(jìn)行Socket通信,同時封裝了一個Peer()函數(shù)返回它的引用。
二.重載相應(yīng)回調(diào)處理函數(shù)
?
Cpp代碼??
ACE_SOCK_Stream&?Client::Peer()?? {?? ????return?peer;?? }?? ?? ACE_HANDLE?Client::get_handle(void)?const?? {?? ????return?peer.get_handle();?? }?? ?? int?Client::handle_input?(ACE_HANDLE?fd)?? {?? ????int?rev=0;?? ????if((rev?=?peer.recv(buffer,1000))>0)?? ????{?? ????????buffer[rev]='\0';?? ????????cout<<endl<<"rev:\t"<<buffer<<endl;?? ????????return?0;?? ????}?? ????else????//Socket連接發(fā)生錯誤,返回-1,在Reactor中注銷事件,觸發(fā)handle_close函數(shù)?? ????{?? ????????return?-1;?? ????}?? }?? ?? int?Client::handle_close?(ACE_HANDLE?handle,?? ?????????????????????????ACE_Reactor_Mask?close_mask)?? {?? ????cout<<endl<<"connecetd?closed";?? ????return?ACE_Event_Handler::handle_close(handle,close_mask);?? }?? ? ?
幾個函數(shù)的功能都非常簡單,這里就不多做介紹了。
三.在Reactor中注冊事件
首先讓我們來看看相應(yīng)的main函數(shù)的代碼:
?
Cpp代碼??
int?main(int?argc,?char?*argv[])??? {?? ????Client?client;?? ????ACE_SOCK_Connector?connector;?? ????ACE_INET_Addr?addr(3000,"127.0.0.1");?? ????ACE_Time_Value?timeout(5,0);?? ????if(connector.connect(client.Peer(),addr,&timeout)?!=?0)?? ????{?? ????????cout<<endl<<"connecetd?fail";?? ????????return?0;?? ????}?? ?? ????ACE_Reactor::instance()->register_handler(&client,ACE_Event_Handler::READ_MASK);?? ?? while(true)?? {?? ACE_Reactor::instance()->handle_events();??? }?? ?? return?0;??? }?? ? ?
在這里可以看到,使用Reactor框架后,依然首先通過ACE_SOCK_Connector的connect函數(shù)來建立連接。建立連接后,可以通過ACE_Reactor::instance()->register_handler函數(shù)來實(shí)現(xiàn)Reactor的注冊,實(shí)現(xiàn)I/O事件和Client對象的handle_input方法相關(guān)聯(lián),它的第一個參數(shù)是事件處理器的地址,第二個參數(shù)是事件類型,由于這里只關(guān)心讀就緒事件,故注冊的事件類型是ACE_Event_Handler::READ_MASK。
四.啟動Reactor事件循環(huán)
通過如上設(shè)置后,我們就可以通過ACE_Reactor::instance()->handle_events()啟動Reactor循環(huán)了,這樣,每當(dāng)服務(wù)器端有數(shù)據(jù)發(fā)送給客戶端時,當(dāng)客戶端的數(shù)據(jù)就緒時,就回觸發(fā)Client對象的handle_input函數(shù),將接收的數(shù)據(jù)打印出來。
通常的做法是,將Reactor事件循環(huán)作為一個單獨(dú)的線程來處理,這樣就不會阻塞main函數(shù)。
五.注銷Reactor事件
Reactor事件的注銷一般有兩種方式,顯式和隱式,下面將分別給予介紹。
隱式注銷。
當(dāng)Reactor捕獲事件后,會觸發(fā)相應(yīng)的"handle_"處理函數(shù),當(dāng)"handle_"處理函數(shù)返回值大于或等于0時,表示處理事件成功,當(dāng)返回值小于0時,表示處理事件失敗,這時Reactor會自動注銷該句柄所注冊的所有事件,并觸發(fā)handle_close函數(shù),以執(zhí)行相應(yīng)的資源清理工作。
在本例中,當(dāng)handle_input函數(shù)里的recv函數(shù)接收到0個數(shù)時,說明socket發(fā)生錯誤(大多為Socket連接中斷),此時返回-1,則系統(tǒng)自動注銷相應(yīng)事件。 顯示注銷。
調(diào)用Reactor對象的remove_handler方法移除,它有兩個參數(shù),第一個是所注冊的事件反應(yīng)器對象,第二個是所要注銷的事件。 在這個示例程序里,連接方只有一個Socket連接,Reactor的優(yōu)勢并沒有體現(xiàn)出來,但在一些網(wǎng)絡(luò)管理系統(tǒng)里,連接方需要對多個需要管理的設(shè)備(服務(wù)器端)進(jìn)行連接,在這種情況下使用Reactor模式,只需要多開一個Reactor事件循環(huán)線程就能實(shí)現(xiàn)事件多路分發(fā)復(fù)用,并且不會阻塞,通過面向?qū)ο蟮幕卣{(diào)方式管理,使用起來非常方便。
Reactor框架的另外一個常用的地方就是服務(wù)器端,一般是一個服務(wù)器端對應(yīng)多個客戶端,這樣用Reactor模式能大幅提高并發(fā)能力,這方面的編程方法將在下一章給與介紹。
?
在服務(wù)器端使用Reactor框架
使用Reactor框架的服務(wù)器端結(jié)構(gòu)如下:
服務(wù)器端注冊兩種事件處理器,ClientAcceptor和ClientService,ClientService類負(fù)責(zé)和客戶端的通信,每一個ClientService對象對應(yīng)一個客戶端的Socket連接。ClientAcceptor專門負(fù)責(zé)被動接受客戶端的連接,并創(chuàng)建ClientService對象。這樣,在一個N個Socket連接的服務(wù)器程序中,將存在1個ClientAcceptor對象和N個ClientService對象。
整個服務(wù)器端流程如下:
首先創(chuàng)建一個ClientAcceptor對象,該對象在Reactor上注冊ACCEPT_MASK事件,Reactor將自動在監(jiān)聽端口建立Socket監(jiān)聽。 如果有對該端口的Socket連接時,Reactor將自動回調(diào)handle_input方法,ClientAcceptor重載此方法,并創(chuàng)建一個ClientService對象,用于處理和Client的通信。 ClientService對象根據(jù)服務(wù)器的具體功能實(shí)現(xiàn),其處理過程和客戶端程序類似,注冊相應(yīng)的回調(diào)事件并分發(fā)即可。 代碼如下:
?
Cpp代碼??
#include?<ace/OS.h>?? #include?<ace/Reactor.h>?? #include?<ace/SOCK_Connector.h>??? #include?<ace/SOCK_Acceptor.h>??? #include?<ace/Auto_Ptr.h>?? ?? class?ClientService?:?public?ACE_Event_Handler?? {?? public:?? ????ACE_SOCK_Stream?&peer?(void)?{?return?this->sock_;?}?? ?? ????int?open?(void)?? ????{?? ????????//注冊讀就緒回調(diào)函數(shù)?? ????????return?this->reactor?()->register_handler(this,?ACE_Event_Handler::READ_MASK);?? ????}?? ?? ????virtual?ACE_HANDLE?get_handle?(void)?const?{?return?this->sock_.get_handle?();?}?? ?? ????virtual?int?handle_input?(ACE_HANDLE?fd?)?? ????{?? ????????//一個簡單的EchoServer,將客戶端的信息返回?? ????????int?rev?=?peer().recv(buf,100);?? ????????if(rev<=0)?? ????????????return?-1;?? ?? ????????peer().send(buf,rev);?? ????????return?0;?? ????}?? ?? ????//?釋放相應(yīng)資源?? virtual?int?handle_close?(ACE_HANDLE,?ACE_Reactor_Mask?mask)?? ????{?? ????????if?(mask?==?ACE_Event_Handler::WRITE_MASK)?? ????????????return?0;?? ????????mask?=?ACE_Event_Handler::ALL_EVENTS_MASK?|?? ????????????ACE_Event_Handler::DONT_CALL;?? ????????this->reactor?()->remove_handler?(this,?mask);?? ????????this->sock_.close?();?? ????????delete?this;????//socket出錯時,將自動刪除該客戶端,釋放相應(yīng)資源?? ????????return?0;?? ????}?? ?? protected:?? ????char?buf[100];?? ????ACE_SOCK_Stream?sock_;?? };?? ?? class?ClientAcceptor?:?public?ACE_Event_Handler?? {?? public:?? ????virtual?~ClientAcceptor?(){this->handle_close?(ACE_INVALID_HANDLE,?0);}?? ?? ????int?open?(const?ACE_INET_Addr?&listen_addr)?? ????{?? ????????if?(this->acceptor_.open?(listen_addr,?1)?==?-1)?? ????????{?? ????????????ACE_OS::printf("open?port?fail");?? ????????????return?-1;?? ????????}?? ????????//注冊接受連接回調(diào)事件?? ????????return?this->reactor?()->register_handler(this,?ACE_Event_Handler::ACCEPT_MASK);?? ????}?? ?? ????virtual?ACE_HANDLE?get_handle?(void)?const?? ????{?return?this->acceptor_.get_handle?();?}?? ?? ????virtual?int?handle_input?(ACE_HANDLE?fd?)?? ????{?? ????????ClientService?*client?=?new?ClientService();?? ????????auto_ptr<ClientService>?p?(client);?? ?? ????????if?(this->acceptor_.accept?(client->peer?())?==?-1)?? ????????{?? ????????????ACE_OS::printf("accept?client?fail");?? ????????????return?-1;?? ????????}?? ????????p.release?();?? ????????client->reactor?(this->reactor?());?? ????????if?(client->open?()?==?-1)?? ????????????client->handle_close?(ACE_INVALID_HANDLE,?0);?? ????????return?0;?? ????}?? ?????? ????virtual?int?handle_close?(ACE_HANDLE?handle,?? ????????ACE_Reactor_Mask?close_mask)?? ????{?? ????????if?(this->acceptor_.get_handle?()?!=?ACE_INVALID_HANDLE)?? ????????{?? ????????????ACE_Reactor_Mask?m?=?ACE_Event_Handler::ACCEPT_MASK?|?? ????????????????ACE_Event_Handler::DONT_CALL;?? ????????????this->reactor?()->remove_handler?(this,?m);?? ????????????this->acceptor_.close?();?? ????????}?? ????????return?0;?? ????}?? ?? protected:?? ????ACE_SOCK_Acceptor?acceptor_;?? };?? ?? int?main(int?argc,?char?*argv[])??? {?? ????ACE_INET_Addr?addr(3000,"192.168.1.142");?? ????ClientAcceptor?server;?? ????server.reactor(ACE_Reactor::instance());?? ????server.open(addr);?? ?? ????while(true)?? ????{?? ????????ACE_Reactor::instance()->handle_events();??? ????}?? ?? ????return?0;??? }?? ? ?
代碼功能比較簡單,需要注意以下幾點(diǎn):
這里注冊事件的方式和前面的文章中方式不一樣,是通過ACE_Event_Handler類的reactor()方法設(shè)置和獲取reactor的指針,比較直觀和方便。前面的文章是通過ACE_Reactor::instance()來獲取的一個單體reactor的指針。 當(dāng)客戶端Socket連接關(guān)閉時,需要釋放相應(yīng)資源,需要注意一下ClientService對象的handle_close方法中釋放資源的相應(yīng)代碼。
定時器的實(shí)現(xiàn)
通過Reactor機(jī)制,還可以很容易的實(shí)現(xiàn)定時器的功能,使用方式如下。
編寫一個事件反應(yīng)器,重載handle_timeout()方法,該方法是定時器的觸發(fā)時間到時,會自動觸發(fā)該方法。 通過Reactor的schedule_timer()方法注冊定時器。 啟動reacotr的handle_events()事件分發(fā)循環(huán)。 當(dāng)不想使用定時器時,可以通過Reactor的cancel_timer()方法注銷定時器。 下面的代碼簡單的實(shí)現(xiàn)了一個定時器,并具有基本的開啟,關(guān)閉功能。
?
Cpp代碼??
#include?<ace/OS.h>?? #include?<ace/Reactor.h>?? ?? class?MyTimerHandler?:?public?ACE_Event_Handler?? {?? private:?? ????int?inteval;????//執(zhí)行時間間隔?? ????int?delay;????????//延遲執(zhí)行時間?? ????int?timerid;?? ?? public:?? ????MyTimerHandler(int?delay,int?inteval)?? ????{?? ????????this->delay=delay;?? ????????this->inteval=inteval;?? ????}?? ?? ????int?open()????//注冊定時器?? ????{?? ????????ACE_Time_Value?delaytime(inteval);?? ????????ACE_Time_Value?intevaltime(inteval);?? ????????timerid?=?reactor()->schedule_timer(this,?? ????????????0,????//傳遞handle_timeout給的參數(shù)?? ????????????delaytime,?? ????????????intevaltime);?? ????????return?timerid;?? ????}?? ?? ????int?close()????//取消定時器?? ????{?? ????????return?reactor()->cancel_timer(timerid);?? ????}?? ?? ????//定時器回調(diào)函數(shù)?? ????int?handle_timeout?(const?ACE_Time_Value?¤t_time,?? ????????const?void?*?=?0)?? ????{?? ????????time_t?epoch?=?((timespec_t)current_time).tv_sec;?? ????????ACE_DEBUG?((LM_INFO,?? ????????????ACE_TEXT?("handle_timeout:?%s\n"),?? ????????????ACE_OS::ctime?(&epoch)));?? ????????return?0;?? ????}?? };?? ?? int?main(int?argc,?char?*argv[])??? {?? ????MyTimerHandler?*?timer?=?new?MyTimerHandler?(3,5);?? ????timer->reactor(ACE_Reactor::instance());?? ????timer->open();?? ?? ????for(int?i=0;i<2;i++)????//觸發(fā)次handle_timeout事件?? ????{?? ????????ACE_OS::printf("\n%d\n",i);?? ????????ACE_Reactor::instance()->handle_events();?? ????}?? ?? ????timer->close();?? ????ACE_OS::printf("cancel?timer");?? ????while(true)?? ????????ACE_Reactor::instance()->handle_events();?? ????return?0;??? }?? ? ?
代碼功能比較簡單,這里就不多做介紹了。
總結(jié)
以上是生活随笔為你收集整理的ACE反应器(Reactor)模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。