BIO桶略、NIO语淘、AIO

一、Linux 基礎(chǔ)知識回顧

1. 用戶空間和內(nèi)核空間

現(xiàn)在操作系統(tǒng)都采用虛擬尋址际歼,處理器先產(chǎn)生一個虛擬地址惶翻,通過地址翻譯成物理地址(內(nèi)存的地址),再通過總線的傳遞鹅心,最后處理器拿到某個物理地址返回的字節(jié)维贺。

對32位操作系統(tǒng)而言,它的尋址空間(虛擬存儲空間)為4G(2的32次方)巴帮。

操作系統(tǒng)的核心是內(nèi)核溯泣,獨立于普通的應(yīng)用程序虐秋,可以訪問受保護的內(nèi)存空間,也有訪問底層硬件設(shè)備的所有權(quán)限垃沦。

為了保證用戶進程不能直接操作內(nèi)核(kernel)客给,保證內(nèi)核的安全,操心系統(tǒng)將虛擬空間劃分為兩部分肢簿,一部分為內(nèi)核空間靶剑,一部分為用戶空間。
針對linux操作系統(tǒng)而言:
將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF)池充,供內(nèi)核使用桩引,稱為內(nèi)核空間。

而將較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF)收夸,供各個進程使用坑匠,稱為用戶空間。

2. 直接I/O和緩存I/O

文件系統(tǒng)IO 分為 DirectIO(直接I/O)和 BufferIO(緩存 I/O)卧惜,其中 BufferIO 也叫Normal IO(標(biāo)準(zhǔn) I/O)厘灼。

大多數(shù)文件系統(tǒng)的默認 I/O 操作都是緩存 I/O。

緩存 I/O

讀操作:操作系統(tǒng)檢查內(nèi)核的緩沖區(qū)有沒有需要的數(shù)據(jù)咽瓷,如果已經(jīng)緩存了设凹,那么就直接從緩存中返回;否則從磁盤中讀取茅姜,然后緩存在操作系統(tǒng)的緩存中闪朱。
寫操作:將數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間的緩存中。這時對用戶程序來說寫操作就已經(jīng)完成钻洒,至于什么時候再寫到磁盤中由操作系統(tǒng)決定监透,除非顯示地調(diào)用了sync同步命令。

以 write 為例航唆,數(shù)據(jù)會先被拷貝進程緩沖區(qū)胀蛮,在拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中,然后才會寫到存儲設(shè)備中糯钙。


17148608e67a9710.png

直接 I/O(少了拷貝到應(yīng)用進程緩沖區(qū)這一步)

1714866ae6f18e99.png

3.阻塞與同步

1 ) 阻塞(Block) / 非租塞(NonBlock)

阻塞和非阻塞是進程在訪問數(shù)據(jù)的時候粪狼,數(shù)據(jù)是否準(zhǔn)備就緒的一種處理方式,比如當(dāng)數(shù)據(jù)沒有準(zhǔn)備就緒的時候
阻塞:往往需要等待緩沖區(qū)中的數(shù)據(jù)準(zhǔn)備好過后才處理其他的事情任岸,否則一直等待在那里再榄。
非阻塞:當(dāng)我們的進程訪問我們的數(shù)據(jù)緩沖區(qū)的時候,如果數(shù)據(jù)沒有準(zhǔn)備好則直接返回享潜,不會等待困鸥。如果數(shù)據(jù)已經(jīng)準(zhǔn)備好,也直接返回。
阻塞和非阻塞關(guān)注的是程序在等待結(jié)果(消息疾就,返回值)時的狀態(tài)澜术。

2 ) 同步(Synchronization) / 異步(Asynchronization)

同步和異步都是基于應(yīng)用程序私操作系統(tǒng)處理IO事件所采用的方式,比如
同步:是應(yīng)用程序要直接參與IO讀寫的操作猬腰。
異步:所有的IO讀寫交給操作系統(tǒng)去處理鸟废,應(yīng)用程序只需要等待通知。

同步方式在處理IO事件的時候姑荷,必須阻塞在某個方法上面等待我們的IO事件完成(阻塞IO事件或者通過輪詢IO事件的方式)盒延。

對于異步來說,所有的IO讀寫都交給了操作系統(tǒng)鼠冕。這個時候添寺,我們可以去做其他的事情,并不需要去完成真正的IO操作懈费,當(dāng)操作完成IO后.會給我們的應(yīng)用程序一個通知计露。
同步和異步關(guān)注的是消息通信機制。

二楞捂、常見 IO 模型

對于一次IO訪問,它會經(jīng)歷兩個階段:

  1. 等待數(shù)據(jù)準(zhǔn)備就緒 (Waiting for the data to be ready)
  2. 操作:將數(shù)據(jù)從內(nèi)核拷貝到進程中 (Copying the data from the kernel to the process)

舉例來說:
讀函數(shù):分為等待系統(tǒng)可讀和真正的讀趋厉。
寫函數(shù):分為等待網(wǎng)卡可以寫和真正的寫寨闹。

說明:
等待就緒的阻塞是不使用 CPU 的,是在“空等”君账。

而真正的讀寫操作的阻塞是使用 CPU 的繁堡,真正在“干活”,而且這個過程非诚缡快椭蹄,屬于memory copy,寬帶通常在 1GB/s 級別以上净赴,可以理解為基本不耗時绳矩。
下圖是幾種常見I/O模型的對比:

17148c6cff70c9b3.png

以socket.read()為例子:

傳統(tǒng)的BIO里面socket.read(),如果TCP RecvBuffer里沒有數(shù)據(jù)玖翅,函數(shù)會一直阻塞翼馆,直到收到數(shù)據(jù),返回讀到的數(shù)據(jù)金度。

對于NIO应媚,如果TCP RecvBuffer有數(shù)據(jù),就把數(shù)據(jù)從網(wǎng)卡讀到內(nèi)存猜极,并且返回給用戶中姜;反之則直接返回0,永遠不會阻塞跟伏。

最新的AIO(Async I/O)里面會更進一步:不但等待就緒是非阻塞的丢胚,就連數(shù)據(jù)從網(wǎng)卡到內(nèi)存的過程也是異步的翩瓜。

換句話說,BIO里用戶最關(guān)心“我要讀”嗜桌,NIO里用戶最關(guān)心"我可以讀了"奥溺,在AIO模型里用戶更需要關(guān)注的是“讀完了”。

NIO一個重要的特點是:socket主要的讀骨宠、寫浮定、注冊和接收函數(shù),在等待就緒階段都是非阻塞的层亿,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)桦卒。

三、什么是 BIO匿又、NIO训柴、AIO

1. 同步阻塞I/O(BIO)

同步阻塞I/O朵锣,服務(wù)器實現(xiàn)模式為一個連接一個線程,即客戶端有連接請求時服務(wù)器就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷火邓,可以通過線程池機制來改善。

BIO方式適用于連接數(shù)目比較小且固定的架構(gòu)塑顺,這種方式對服務(wù)端資源要求比較高须误,并發(fā)局限于應(yīng)用中,在jdk1.4以前是唯一的io選擇旭绒,但程序直觀簡單易理解鸟妙。

BIO圖解:

1714886a43518adf.png

偽異步模型IO
也被成為M:N客戶服務(wù)模型。即通過線程池模型的形式用M個線程來服務(wù)N個客戶端的連接挥吵;
其中M的大小可以根據(jù)服務(wù)器的配置來設(shè)置最大值重父,而可服務(wù)客戶端個數(shù)N則可以遠遠的大于M.
這樣來提高服務(wù)器的服務(wù)效率,提高線程利用率忽匈。
同BIO模型類似房午,只不過,Acceptor接受客戶端請求后丹允,不再獨立啟動線程來處理歪沃,而是將客戶請求交給線程池來處理,從而減少線程的創(chuàng)建數(shù)量嫌松,提高線程利用率沪曙,增加服務(wù)器的處理能力;
偽異步IO圖解

171488b9be8aa032.png

2. 同步非阻塞I/O(NIO)

同步非阻塞I/O萎羔,服務(wù)器實現(xiàn)模式為一個請求一個線程液走,即客戶端發(fā)送的連接請求都會注冊到多路復(fù)用器上,多路復(fù)用器輪詢到連接有IO請求時才啟動一個線程進行處理。

NIO方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu)缘眶,比如聊天服務(wù)器嘱根,并發(fā)局限于應(yīng)用中,編程比較復(fù)雜巷懈,jdk1.4開始支持该抒。

I/O多路復(fù)用模型

I/O多路復(fù)用:I/O就是指的我們網(wǎng)絡(luò)I/O,多路指多個TCP連接(或多個Channel),復(fù)用指復(fù)用一個或少量線程顶燕。

串起來理解就是很多個網(wǎng)絡(luò)I/O復(fù)用一個或少量的線程來處理這些連接凑保。

多路復(fù)用的優(yōu)勢并不是單個連接處理的更快,而是在于能處理更多的連接涌攻。


171492ec133e9e39.png

目前的常用的IO復(fù)用模型有三種:select欧引,poll,epoll恳谎。

I/O多路復(fù)用就是通過一種機制芝此,一個進程可以監(jiān)視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒)因痛,能夠通知程序進行相應(yīng)的讀寫操作婚苹。

select,poll鸵膏,epoll本質(zhì)上都是同步I/O膊升,因為他們都需要在讀寫事件就緒后自己負責(zé)進行讀寫,也就是說這個讀寫過程是阻塞的较性,而異步I/O則無需自己負責(zé)進行讀寫用僧,異步I/O的實現(xiàn)會負責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間结胀。

jdk1.4 是使用的 select/poll 模型
jdk1.5 以后把 select/poll 改為了epoll模型

1) select 模型

各個客戶端連接的文件描述符(fd)也就是套接字赞咙,都被放到了一個集合中,調(diào)用select函數(shù)之后會一直監(jiān)視這些文件描述符中有哪些可讀糟港,如果有可讀的描述符那么我們的工作進程就去讀取資源攀操。

我們在select函數(shù)中告訴內(nèi)核需要監(jiān)聽的不同狀態(tài)的文件描述符以及能接受的超時時間,函數(shù)會返回所有狀態(tài)下就緒的描述符的個數(shù)秸抚,并且可以通過遍歷fdset速和,來找到就緒的文件描述符。
存在的問題:

  • 每次調(diào)用select剥汤,都需要把待監(jiān)控的fd集合從用戶態(tài)拷貝到內(nèi)核態(tài)颠放,當(dāng)fd很大時,開銷很大吭敢。
  • 每次調(diào)用select碰凶,都需要輪詢一遍所有的fd,查看就緒狀態(tài)。這個開銷在fd很多時也很大欲低。
  • select支持的最大文件描述符數(shù)量有限辕宏,默認是1024

2) poll 模型

相對 于select,poll 已不存在最大文件描述符限制砾莱。

3) epoll 模型

epoll在Linux2.6內(nèi)核正式提出瑞筐,是基于事件驅(qū)動的I/O方式

相對于select來說,epoll沒有描述符個數(shù)限制腊瑟,使用一個文件描述符管理多個描述符聚假,將用戶關(guān)心的文件描述符的事件存放到內(nèi)核的一個事件表中,這樣在用戶空間和內(nèi)核空間的copy只需一次(可以理解為一塊公共內(nèi)存扫步,該內(nèi)存既不屬于用戶態(tài)也不屬于內(nèi)核態(tài))魔策。

select/poll的增強版本,它能顯著提高程序在大量并發(fā)連接中只有少量活躍的情況下的系統(tǒng)CPU利用率河胎。原因就是獲取事件的時候闯袒,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內(nèi)核IO事件異步喚醒而加入Ready隊列的描述符集合就行了游岳。

epoll 的好處:

  • 避免內(nèi)存級拷貝
  • 事件驅(qū)動(不是輪詢)

但是也并不是所有情況下 epoll 都比 select/poll 好政敢,比如在如下場景:
在大多數(shù)客戶端都很活躍的情況下,系統(tǒng)會把所有的回調(diào)函數(shù)都喚醒胚迫,所以會導(dǎo)致負載較高喷户。既然要處理這么多的連接,那倒不如 select/poll 遍歷簡單有效访锻。

4)select & poll & epoll 比較

select poll epoll
操作方式 遍歷 遍歷 回調(diào)
底層實現(xiàn) 數(shù)組 鏈表 哈希表
IO 效率 每次調(diào)用都進行線性遍歷褪尝,時間復(fù)雜度為O(n) 每次調(diào)用都進行線性遍歷,時間復(fù)雜度為O(n) 事件通知方式期犬,每當(dāng)fd就緒河哑,系統(tǒng)注冊的回調(diào)函數(shù)就會被調(diào)用,將就緒fd放到readyList里面龟虎,時間復(fù)雜度O(1)
最大連接數(shù) 1024 無上限 無上限
fd 拷貝 每次調(diào)用select璃谨,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài) 每次調(diào)用poll,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài) 調(diào)用epoll_ctl時拷貝進內(nèi)核并保存鲤妥,之后每次epoll_wait不拷貝

NIO的3個核心概念

1) 緩沖區(qū)Buffer

Buffer是一個對象佳吞。它包含一些要寫入或者讀出的數(shù)據(jù)。在面向流的I/O中棉安,可以將數(shù)據(jù)寫入或者將數(shù)據(jù)直接讀到Stream對象中底扳。

在NIO中,所有的數(shù)據(jù)都是用緩沖區(qū)處理贡耽。IO是面向流的衷模,NIO是面向緩沖區(qū)的羡滑。

最常用的緩沖區(qū)是ByteBuffer,一個ByteBuffer提供了一組功能用于操作byte數(shù)組算芯。除了ByteBuffer柒昏,還有其他的一些緩沖區(qū),事實上熙揍,每一種Java基本類型(除了Boolean)都對應(yīng)一種緩沖區(qū)职祷,具體如下:

  • ByteBuffer:字節(jié)緩沖區(qū)
  • CharBuffer:字符緩沖區(qū)
  • ShortBuffer:短整型緩沖區(qū)
  • IntBuffer:整型緩沖區(qū)
  • LongBuffer:長整型緩沖區(qū)
  • FloatBuffer:浮點型緩沖區(qū)
  • DoubleBuffer:雙精度浮點型緩沖區(qū)

2) 通道Channel

Channel是一個通道,可以通過它讀取和寫入數(shù)據(jù)届囚,他就像自來水管一樣有梆,網(wǎng)絡(luò)數(shù)據(jù)通過Channel讀取和寫入。

通道和流不同之處在于通道是雙向的意系,流只是在一個方向移動泥耀,而且通道可以用于讀,寫或者同時用于讀寫蛔添。

因為Channel是全雙工的痰催,所以它比流更好地映射底層操作系統(tǒng)的API,特別是在UNIX網(wǎng)絡(luò)編程中迎瞧,底層操作系統(tǒng)的通道都是全雙工的夸溶,同時支持讀和寫。
Channel有四種實現(xiàn):

  • FileChannel:是從文件中讀取數(shù)據(jù)凶硅。
  • DatagramChannel:從UDP網(wǎng)絡(luò)中讀取或者寫入數(shù)據(jù)缝裁。
  • SocketChannel:從TCP網(wǎng)絡(luò)中讀取或者寫入數(shù)據(jù)。
  • ServerSocketChannel:允許你監(jiān)聽來自TCP的連接足绅,就像服務(wù)器一樣捷绑。每一個連接都會有一個SocketChannel產(chǎn)生。

3) 多路復(fù)用器Selector

Selector選擇器可以監(jiān)聽多個Channel通道感興趣的事情(read氢妈、write粹污、accept(服務(wù)端接收)、connect允懂,實現(xiàn)一個線程管理多個Channel厕怜,節(jié)省線程切換上下文的資源消耗衩匣。

Selector只能管理非阻塞的通道蕾总,F(xiàn)ileChannel是阻塞的,無法管理琅捏。
關(guān)鍵對象
Selector:選擇器對象生百,通道注冊、通道監(jiān)聽對象和Selector相關(guān)柄延。
SelectorKey:通道監(jiān)聽關(guān)鍵字蚀浆,通過它來監(jiān)聽通道狀態(tài)缀程。
監(jiān)聽注冊 監(jiān)聽注冊在Selector

socketChannel.register(selector, SelectionKey.OP_READ);

監(jiān)聽的事件

  • OP_ACCEPT:接收就緒,serviceSocketChannel使用的
  • OP_READ:讀取就緒市俊,socketChannel使用
  • OP_WRITE:寫入就緒杨凑,socketChannel使用
  • OP_CONNECT:連接就緒, socketChannel使用

NIO的應(yīng)用和框架

1) NIO的應(yīng)用

Java NIO成功的應(yīng)用在了各種分布式摆昧、即時通信和中間件Java系統(tǒng)中撩满,充分的證明了基于NIO構(gòu)建的通信基礎(chǔ),是一種高效绅你,且擴展性很強的通信架構(gòu)伺帘。

例如:Dubbo(服務(wù)框架),就默認使用Netty作為基礎(chǔ)通信組件忌锯,用于實現(xiàn)各進程節(jié)點之間的內(nèi)部通信伪嫁。

Jetty、Mina偶垮、Netty张咳、Dubbo、ZooKeeper等都是基于NIO方式實現(xiàn)似舵。

Mina出身于開源界的大牛Apache組織 Netty出身于商業(yè)開源大亨Jboss Dubbo阿里分布式服務(wù)框架

2) NIO框架

特別是Netty是目前最流行的一個Java開源框架NIO框架晶伦,Netty提供異步的、事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用程序框架和工具啄枕,用以快速開發(fā)高性能婚陪、高可靠性的網(wǎng)絡(luò)服務(wù)器和客戶端程序。
相比JDK原生NIO频祝,Netty提供了相對十分簡單易用的API泌参,非常適合網(wǎng)絡(luò)編程。

Mina和Netty這兩個NIO框架的創(chuàng)作者是同一個人Trustin Lee 常空。Netty從某種程度上講是Mina的延伸和擴展沽一,解決了一些Mina上的設(shè)計缺陷,也優(yōu)化了一下Mina上面的設(shè)計理念漓糙。

另一方面Netty相比較Mina的優(yōu)勢:

  • 更容易學(xué)習(xí)
  • API更簡單
  • 詳細的范例源碼和API文檔
  • 更活躍的論壇和社區(qū)
  • 更高的代碼更新維護速度

Netty無疑是NIO框架的首選铣缠,它的健壯性、功能昆禽、性能蝗蛙、可定制性和可擴展性在同類框架都是首屈一指的,后續(xù)將重點詳細談Netty的實現(xiàn)原理以及實戰(zhàn)場景醉鳖。

異步非阻塞I/O(AIO)

服務(wù)器實現(xiàn)模式為一個有效請求一個線程捡硅,客戶端的I/O請求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動線程進行處理。

AIO方式適用于連接數(shù)目多且連接比較長(重操作)的架構(gòu)盗棵,比如相冊服務(wù)器壮韭,充分調(diào)用OS參與并發(fā)操作北发,編程比較復(fù)雜。

AIO又稱為NIO2喷屋,在JDK7才開始支持琳拨。


17149691710f9eaa.png
171496bb01bec308.png

參考

BIO、NIO屯曹、AIO

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末从绘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子是牢,更是在濱河造成了極大的恐慌僵井,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驳棱,死亡現(xiàn)場離奇詭異批什,居然都是意外死亡,警方通過查閱死者的電腦和手機社搅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門驻债,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人形葬,你說我怎么就攤上這事合呐。” “怎么了笙以?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵淌实,是天一觀的道長。 經(jīng)常有香客問我猖腕,道長拆祈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任倘感,我火速辦了婚禮放坏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘老玛。我一直安慰自己淤年,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布蜡豹。 她就那樣靜靜地躺著麸粮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪余素。 梳的紋絲不亂的頭發(fā)上豹休,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天炊昆,我揣著相機與錄音桨吊,去河邊找鬼威根。 笑死,一個胖子當(dāng)著我的面吹牛视乐,可吹牛的內(nèi)容都是我干的洛搀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼佑淀,長吁一口氣:“原來是場噩夢啊……” “哼留美!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伸刃,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谎砾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捧颅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體景图,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年碉哑,在試婚紗的時候發(fā)現(xiàn)自己被綠了挚币。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡扣典,死狀恐怖妆毕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贮尖,我是刑警寧澤笛粘,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站湿硝,受9級特大地震影響闰蛔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜图柏,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一序六、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚤吹,春花似錦例诀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至二驰,卻和暖如春扔罪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桶雀。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工矿酵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唬复,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓全肮,卻偏偏與公主長得像敞咧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辜腺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

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