windows IOCP完成端口實(shí)用詳解

開(kāi)篇之前先放上本次講的IOCP project github地址:這里 缨叫。這個(gè)project中包含了IOCP和select拖刃,各自封裝成一個(gè)動(dòng)態(tài)鏈接庫(kù),可以直接使用脚翘。同時(shí)項(xiàng)目配有完整的glog支持,方便調(diào)試绍哎,并可以通過(guò)config控制server来农。如有bug,歡迎大家提出崇堰,正在完善過(guò)程中沃于,代碼可以優(yōu)化的地方也請(qǐng)大家隨時(shí)提出,一起進(jìn)步成長(zhǎng)海诲。

本文主要從以下幾方面講解IOCP使用及其原理繁莹。

為什么需要完成端口

完成端口能做什么

完成端口原理

如何使用完成端口

1.? 為什么需要完成端口

網(wǎng)絡(luò)通信模型是編寫(xiě)網(wǎng)絡(luò)程序的一個(gè)比較核心的模塊,也直接影響著程序的性能特幔,所以選擇合適的網(wǎng)絡(luò)模型是非常有必要的咨演。

IOCP是一種網(wǎng)絡(luò)通信模型,但是在IOCP出現(xiàn)之前已經(jīng)有相關(guān)網(wǎng)絡(luò)通信模型在使用了蚯斯,比較普遍的應(yīng)該就是select模型薄风,另外windows自己家也單獨(dú)實(shí)現(xiàn)了alertable I/O等。但是提到的select和alertable I/O都存在一些局限溉跃,比如select模型其并發(fā)處理量受FDSETSIZE宏大小限制村刨,在windows平臺(tái)上這個(gè)大小默認(rèn)是64告抄,當(dāng)然也可以自己在包含select之前手動(dòng)#define其值撰茎,但是如果在使用之前就定義一個(gè)很大的值難免有點(diǎn)造成資源浪費(fèi),libevent就提供了一種自由的方法來(lái)使用select打洼,這個(gè)在后面的文章中會(huì)詳細(xì)介紹龄糊。alertable I/O一個(gè)缺陷是多線程之間無(wú)法達(dá)到負(fù)載均衡的,同一個(gè)線程發(fā)出的IO請(qǐng)求必須由同一個(gè)線程來(lái)接收募疮,即使其他線程閑著沒(méi)事干炫惩。所以這不能充分利多核系統(tǒng)的強(qiáng)大資源。那么IOCP有沒(méi)有缺陷呢阿浓,當(dāng)然也有他嚷,首先是使用起來(lái)不夠簡(jiǎn)單明了,接口設(shè)計(jì)的不夠簡(jiǎn)潔直觀芭毙。但是呢性能還是杠杠的筋蓖。

上面簡(jiǎn)單的說(shuō)了IOCP模型與其他模型的一些對(duì)比,另外一點(diǎn)很大的區(qū)別是退敦,IOCP模型是一種真正意義上的異步通信模型粘咖,具體啥是異步啥是同步可以參考我之前的一篇文章。有一點(diǎn)需要說(shuō)明的是侈百,并不是所有網(wǎng)絡(luò)通信項(xiàng)目都必須要使用IOCP模型瓮下,對(duì)于一些已知的連接數(shù)較少的網(wǎng)絡(luò)程序,完全可以用select甚至是每個(gè)客戶端對(duì)應(yīng)一個(gè)線程這種方式。

2.? 完成端口能做什么

上面吹了一大波完成端口浓镜,那么完成端口究竟能做什么呢蜕企。

首先一點(diǎn)是:IOCP會(huì)主動(dòng)幫我們完成網(wǎng)絡(luò)IO數(shù)據(jù)復(fù)制。這一點(diǎn)其實(shí)也就是他與其他網(wǎng)絡(luò)模型最直接的區(qū)別了震缭,一般網(wǎng)絡(luò)操作包括兩個(gè)步驟赂毯,以recv來(lái)說(shuō)吧,如果是一般模型拣宰,那么其第一步是通知等待的線程有數(shù)據(jù)可以讀取党涕,這時(shí)候線程會(huì)調(diào)用recv或者recvfrom等函數(shù)將數(shù)據(jù)從讀緩沖區(qū)復(fù)制到用戶空間,然后再做下一步的處理巡社,而IOCP能幫我們的是膛堤,他會(huì)在內(nèi)核中幫我們監(jiān)聽(tīng)那些我們感興趣的的事件,例如我們希望接收客戶端數(shù)據(jù)晌该,那么我們向完成端口投遞一個(gè)讀事件肥荔,完成端口在監(jiān)測(cè)有讀事件到來(lái)的時(shí)候會(huì)主動(dòng)地去幫我們把數(shù)據(jù)從內(nèi)存空間復(fù)制到用戶空間,然后通知我們過(guò)來(lái)取數(shù)據(jù)就OK了朝群,這就是IOCP提供的方便之處燕耿。

另外一點(diǎn):IOCP在內(nèi)部管理線程,實(shí)現(xiàn)負(fù)載平衡姜胖。上面提到了windows的alertable I/O的負(fù)載均衡是他一個(gè)弊端誉帅,那么IOCP是如何自己管理線程調(diào)度的呢,簡(jiǎn)單的說(shuō)就是以棧的方式進(jìn)行管理右莱,具體內(nèi)容接下來(lái)一節(jié)會(huì)詳細(xì)描述蚜锨。

3.? 完成端口原理

overlapped

提到IOCP就不得不提到overlapped這個(gè)數(shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)結(jié)構(gòu)是IOCP進(jìn)行異步通信的關(guān)鍵慢蜓。

typedef struct _OVERLAPPED {

ULONG_PTR Internal;

ULONG_PTR InternalHigh;

union {

struct {

DWORD Offset;

DWORD OffsetHigh;

};

PVOID? Pointer;

};

HANDLE? ? hEvent;

} OVERLAPPED, *LPOVERLAPPED;

這個(gè)數(shù)據(jù)結(jié)構(gòu)原本windows是不打算公開(kāi)的亚再,隨著軟件編程的發(fā)展后來(lái)微軟的工程師發(fā)現(xiàn)編程人員需要用到這個(gè)數(shù)據(jù)結(jié)構(gòu),所以就把他公開(kāi)了晨抡,但是對(duì)于內(nèi)部的變量名卻沒(méi)有變動(dòng)氛悬,因?yàn)槲④泝?nèi)部使用這個(gè)變量名有太多地方了,如果該變量名可能會(huì)帶來(lái)很多其他的不好的影響耘柱,所以就沒(méi)有更換變量名如捅。具體的變量含義在微軟的官方文檔中已經(jīng)明確給出。

Internal: 這個(gè)變量用來(lái)表明當(dāng)前IO請(qǐng)求的狀態(tài)帆谍,當(dāng)我們向完成端口提交一個(gè)IO請(qǐng)求的時(shí)候如果請(qǐng)求還沒(méi)有響應(yīng)伪朽,這個(gè)值就會(huì)是STATUS_PENDING。

InternalHigh:這個(gè)變量用來(lái)當(dāng)前IO請(qǐng)求字節(jié)數(shù)汛蝙。

Offset:指定文件的起始偏移位置

OffsetHigh:指定開(kāi)始傳輸數(shù)據(jù)的字節(jié)數(shù)的的高位

hEvent:是事件句柄烈涮,在IO請(qǐng)求完成后處于信號(hào)狀態(tài)朴肺。

需要注意的是,在網(wǎng)絡(luò)通信過(guò)程中offser與offserhigh是被系統(tǒng)自動(dòng)忽略的坚洽,這兩個(gè)值在異步讀寫(xiě)文件時(shí)使用戈稿。

在異步IO過(guò)程中,只要向可以使用OVERLAPPED數(shù)據(jù)結(jié)構(gòu)的函數(shù)投遞一個(gè)overlapped數(shù)據(jù)機(jī)構(gòu)讶舰,系統(tǒng)內(nèi)核就會(huì)在后臺(tái)默默的幫你監(jiān)聽(tīng)你所投遞的IO事件鞍盗,當(dāng)你的感興趣的事件觸發(fā)的時(shí)候系統(tǒng)會(huì)在后臺(tái)幫你收集好數(shù)據(jù)然后通知你事件完成,并返回給你你投遞的Overlapped數(shù)據(jù)結(jié)構(gòu)的地址,由此可以看出在時(shí)間完成之前你是不能改變overlappd的地址的跳昼,否則會(huì)出現(xiàn)未定義的行為般甲。

在項(xiàng)目中使用Overlapped數(shù)據(jù)結(jié)構(gòu)一般有兩種方法:

① 使用結(jié)構(gòu)體包含,并通過(guò)CONTAINING_RECORD抽取IO數(shù)據(jù)

struct TEST_OVERLAPPED{

OVERLAPPED overlapped_;

WSABUF? wsabuf_;

char data[SIZE];

int data_len_;

OP operate_type_;

}

定義這樣的數(shù)據(jù)結(jié)構(gòu)鹅颊,在傳入overlapped函數(shù)的時(shí)候?qū)⑵鋸?qiáng)制轉(zhuǎn)換成(OVERLAPPED*)(TEST_OVERLAPPED), 另外OVERLAPPED數(shù)據(jù)結(jié)構(gòu)一定要放在新的數(shù)據(jù)結(jié)構(gòu)頭部敷存。operate_type_是自己定義的一個(gè)標(biāo)識(shí),用來(lái)表示這個(gè)IO請(qǐng)求是什么類(lèi)型的堪伍,當(dāng)然也可以將本次IO請(qǐng)求的socket句柄放進(jìn)去锚烦,用來(lái)表明具體是哪個(gè)client的IO。所有的這些數(shù)據(jù)都可以通過(guò)CONTAINING_RECORD宏來(lái)抽取帝雇,具體CONTAINING_RECORD是如何工作的我就不細(xì)講了涮俄,網(wǎng)上一大堆。

② C++類(lèi)繼承overlapped數(shù)據(jù)結(jié)構(gòu)

這種方法其實(shí)與第一種方法很類(lèi)似尸闸,不過(guò)比較方便的是不需要用CONTAINING_RECORD來(lái)抽取具體信息了彻亲。

class MyOverlapped : public OVERLAPPED{

WSABUF wsabuf_;

char data_[DATASIZE];

int data_len_;

OP operate_type_;

SOCKET client_;

}

同樣在使用的時(shí)候需要將其轉(zhuǎn)換為(OVERLAPPED*)傳入overlapped函數(shù),在IO事件完成后再將其轉(zhuǎn)換為我們的類(lèi)室叉,(MyOverlapped*)(&OVERLAPPED),之后直接讀取成員變量即可得出信息睹栖。

IOCP內(nèi)部工作原理

先上一張Jeffrey Richter在windows核心編程里的一個(gè)IOCP原理圖硫惕。一張好圖的效果比說(shuō)n句話效果要好多了茧痕。


雖然微軟沒(méi)有公開(kāi)完成端口具體的實(shí)現(xiàn)方式,但是從Jeffrey Richter的windows核心編程可以大概了解完成端口的大概實(shí)現(xiàn)恼除。

當(dāng)我們創(chuàng)建一個(gè)完成端口的時(shí)候(創(chuàng)建方式下一節(jié)具體講)windows底層會(huì)幫我們創(chuàng)建一系列底層設(shè)施踪旷,以輔助我們后來(lái)的通信過(guò)程。具體設(shè)施如上圖所示豁辉。

①設(shè)備列表

這里的設(shè)備列表我們可以簡(jiǎn)單的認(rèn)為就是所有連接的socket信息的列表令野,對(duì)于一個(gè)socket我們要將他與完成端口關(guān)聯(lián),完成端口才會(huì)在內(nèi)核中為我們監(jiān)聽(tīng)我們感興趣的事件徽级,這里有一個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)气破,dwCompletionKey,這個(gè)數(shù)據(jù)結(jié)構(gòu)需要在我們將socket綁定到完成端口時(shí)一起傳入內(nèi)核設(shè)備列表中餐抢,那么他有什么作用呢现使?我的理解是這個(gè)數(shù)據(jù)結(jié)構(gòu)主要是為了在內(nèi)核中標(biāo)記當(dāng)前所通信的socket對(duì)象具體是哪一個(gè)低匙,這個(gè)數(shù)據(jù)結(jié)構(gòu)是由我們自己定義的,所以在這個(gè)結(jié)構(gòu)體里我們可以加上我們一些自己想要的信息碳锈,因?yàn)楹髞?lái)通信過(guò)程中內(nèi)核會(huì)將這個(gè)數(shù)據(jù)傳送給我們顽冶,所以在這個(gè)結(jié)構(gòu)中定義一些你感興趣的字段可以方便后期的一些操作,比如你可以定義一個(gè)容器用來(lái)存放改設(shè)備投遞的所有IO操作售碳,這樣在后期該socket關(guān)閉的時(shí)候可以方便清理與其相關(guān)的內(nèi)存强重,以確保不會(huì)造成內(nèi)存泄漏。

②完成隊(duì)列

完成隊(duì)列中存放的是已經(jīng)完成的IO事件贸人,每一個(gè)列表項(xiàng)主要包括dwBytesTransferred, dwCompletionKey, pOverlapped, dwError四個(gè)數(shù)據(jù)间景,dwBytesTransferred顧名思義就是本次IO事件所傳輸?shù)淖止?jié)數(shù),dwCompletionKey就是上面我們所說(shuō)的用來(lái)標(biāo)識(shí)socket信息的數(shù)據(jù)結(jié)構(gòu)艺智,從這個(gè)數(shù)據(jù)結(jié)構(gòu)我們可以知道當(dāng)前的IO事件是發(fā)生在哪個(gè)socket上的拱燃,當(dāng)然你需要在completionKey中設(shè)置這個(gè)字段,否則你也不知道這個(gè)事件是發(fā)生在哪個(gè)socket上的力惯。pOverlapped數(shù)據(jù)結(jié)構(gòu)也就是我們之前提到的Overlapped數(shù)據(jù)結(jié)構(gòu)碗誉,這個(gè)數(shù)據(jù)結(jié)構(gòu)里包含了IO數(shù)據(jù)。

③線程管理設(shè)施

線程管理是完成端口的一大特點(diǎn)父晶,也就是內(nèi)核在內(nèi)部自己管理一個(gè)線程池哮缺。與這個(gè)線程池相關(guān)的基礎(chǔ)設(shè)施主要有等待線程隊(duì)列(棧)(以棧的方式管理),已釋放的線程列表甲喝,已暫停的線程列表尝苇。當(dāng)線程調(diào)用GetQueuedCompletionStatus的時(shí)候內(nèi)核會(huì)將該線程放入線程棧中,因?yàn)镚etQueuedCompletionStatus會(huì)將線程掛起埠胖,一旦有事件發(fā)生GetQueuedCompletion返回糠溜,這時(shí)候內(nèi)核會(huì)將線程放入已釋放列表中,如果這個(gè)已釋放的線程又調(diào)用了一些函數(shù)將線程掛起直撤,那么內(nèi)核會(huì)將其放入已暫停線程列表中非竿。當(dāng)GetQueuedCompletionStatus返回后線程處理完數(shù)據(jù)后再次調(diào)用GetQueuedCompletionStatus進(jìn)行等待時(shí),內(nèi)核會(huì)重新將該線程放到線程等待棧中谋竖,這樣的一個(gè)流程下來(lái)我們可以看出红柱,如果在IO事件處理比較慢的情況下一個(gè)線程就可以搞定所有的IO請(qǐng)求,這樣避免了線程之間的上下文切換帶來(lái)的性能開(kāi)銷(xiāo)蓖乘。

4. 如何使用完成端口

鋪墊了這么多锤悄,終于要講到如何使用IOCP了。先介紹一下使用完成端口需要用到的幾個(gè)比較重要的函數(shù)嘉抒。

(1)CreateIOCompletionPort

之前說(shuō)到過(guò)完成端口在API設(shè)計(jì)上不夠清晰零聚,現(xiàn)在提到的這個(gè)函數(shù)可以充分說(shuō)明這個(gè)問(wèn)題。這個(gè)函數(shù)有兩個(gè)用途。

①CreateIOCompletionPort(-1,NULL,NULL,0)

這種調(diào)用方法是用來(lái)創(chuàng)建一個(gè)新的完成端口時(shí)使用的方式隶症,最重要的是第四個(gè)參數(shù)容诬,第四個(gè)參數(shù)主要是用來(lái)確定在同一時(shí)間最多能有多少個(gè)線程運(yùn)行,設(shè)置為0代表數(shù)量與機(jī)器CPU個(gè)數(shù)一致沿腰。

②CreateIOCompletionPort(socket, completionport览徒, pcompletionkey, 0)

這種調(diào)用方式使用老將一個(gè)socket句柄與已創(chuàng)建好的完成端口相關(guān)聯(lián),第一個(gè)參數(shù)代表句柄創(chuàng)建的時(shí)候不需要傳入颂龙,這個(gè)主要是用于將一個(gè)句柄綁定到完成端口時(shí)時(shí)使用的习蓬,第二個(gè)參數(shù)代表已創(chuàng)建好的完成端口,第三個(gè)參數(shù)completionley上面已經(jīng)說(shuō)過(guò)了措嵌,是用來(lái)標(biāo)識(shí)一個(gè)socket句柄的躲叼。

(2)GetQueuedCompletionStatus(

_In_? HANDLE? ? ? CompletionPort,

_Out_ LPDWORD? ? ? lpNumberOfBytes,

_Out_ PULONG_PTR? lpCompletionKey,

_Out_ LPOVERLAPPED *lpOverlapped,

_In_? DWORD? ? ? ? dwMilliseconds)

這個(gè)函數(shù)主要是將當(dāng)前線程掛起等待IO事件完成。

CompletionPort:就是上面所創(chuàng)建的端口企巢,另外有一點(diǎn)要說(shuō)明的是這個(gè)端口與socket中使用的端口不是一個(gè)概念枫慷,你就把這個(gè)端口當(dāng)成一個(gè)內(nèi)核句柄就行。

lpNumberOfBytes:這個(gè)代表IO事件傳輸?shù)淖止?jié)數(shù)

lpCompletionKey:代表當(dāng)前IO事件所隸屬的句柄信息浪规,這個(gè)是我們?cè)诮壎ň浔酵瓿啥丝跁r(shí)自己傳進(jìn)去的或听。

lpOverlapped:這個(gè)值就包含了這次異步IO的數(shù)據(jù)信息。

dwMilliseconds:代表在等待一個(gè)IO事件完成時(shí)會(huì)等多久笋婿,如果這個(gè)值設(shè)為INFINITE誉裆,那么這個(gè)調(diào)用將永遠(yuǎn)不會(huì)超時(shí),如果傳入0缸濒,那么這個(gè)調(diào)用會(huì)立即返回足丢。

(3)PostQueuedCompletionStatus(

_In_? ? HANDLE? ? ? CompletionPort,

_In_? ? DWORD? ? ? ? dwNumberOfBytesTransferred,

_In_? ? ULONG_PTR? ? dwCompletionKey,

_In_opt_ LPOVERLAPPED lpOverlapped

)

這個(gè)函數(shù)可以用來(lái)模擬IO完成事件,經(jīng)常用于退出時(shí)發(fā)送一個(gè)模擬的IO完成事件來(lái)喚醒在等待中的線程庇配,參數(shù)信息之前都有提到就不解釋了斩跌。

以上三個(gè)就是使用完成端口時(shí)最主要的三個(gè)函數(shù),那么既然要用到Overlapped數(shù)據(jù)結(jié)構(gòu)來(lái)投遞IO請(qǐng)求事件捞慌,那么socket的發(fā)送接收函數(shù)也就不能用原來(lái)常用的send和recv了耀鸦,要用到WSAERecv,WSASend兩個(gè)函數(shù)了,因?yàn)檫@兩個(gè)函數(shù)都接收一個(gè)overlapped參數(shù)卿闹。另外還有一個(gè)要提到的是揭糕,accept沒(méi)有對(duì)應(yīng)的WSA版本萝快,但是由于accept是一個(gè)阻塞函數(shù)锻霎,所以如果想盡可能提高性能,可以使用微軟后期自己封裝的一個(gè)函數(shù)acceptex揪漩,這個(gè)函數(shù)與原始的accept之間的差別在于它是將已經(jīng)創(chuàng)建好的socket傳入函數(shù)旋恼,那么當(dāng)其IO事件返回,對(duì)應(yīng)的socket也就已經(jīng)與客戶端建立連接了奄容,這個(gè)函數(shù)也接受一個(gè)overlapped數(shù)據(jù)結(jié)構(gòu)冰更,所以我們可以把a(bǔ)ccept事件當(dāng)做一般的IO事件即可产徊,當(dāng)GetQueuedCompletionStatus返回時(shí)檢查completionkey中的socket句柄是不是listen socket,如果是listen socket說(shuō)明有新的連接接入蜀细,這時(shí)候需要調(diào)用微軟自己的函數(shù)GetAcceptExSockaddrs來(lái)獲取客戶端的地址信息舟铜。在創(chuàng)建好完成端口時(shí)可以先投遞幾個(gè)accept IO事件,這樣可以在高并發(fā)的時(shí)候處理的得心應(yīng)手奠衔,當(dāng)然了谆刨,完成端口本身就很強(qiáng)大。有一點(diǎn)要注意的是在每次處理完新的連接時(shí)要重新投遞新的accept事件归斤,為下一個(gè)連接做準(zhǔn)備痊夭。

IOCP具體如何使用可以看我的GitHub,里面有完整的完成端口項(xiàng)目,對(duì)overlapped與completionkey都做了封裝脏里,有資源管理器她我,測(cè)試還未發(fā)現(xiàn)資源泄露,有問(wèn)題可以直接提出來(lái)迫横,會(huì)及時(shí)改進(jìn)番舆。

完成端口使用流程圖:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市矾踱,隨后出現(xiàn)的幾起案子合蔽,更是在濱河造成了極大的恐慌,老刑警劉巖介返,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拴事,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡圣蝎,警方通過(guò)查閱死者的電腦和手機(jī)刃宵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)徘公,“玉大人牲证,你說(shuō)我怎么就攤上這事」孛妫” “怎么了坦袍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)等太。 經(jīng)常有香客問(wèn)我捂齐,道長(zhǎng),這世上最難降的妖魔是什么缩抡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任奠宜,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘压真。我一直安慰自己娩嚼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布滴肿。 她就那樣靜靜地躺著岳悟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泼差。 梳的紋絲不亂的頭發(fā)上竿音,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音拴驮,去河邊找鬼春瞬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛套啤,可吹牛的內(nèi)容都是我干的宽气。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼潜沦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼萄涯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起唆鸡,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涝影,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后争占,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體燃逻,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年臂痕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伯襟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡握童,死狀恐怖姆怪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情澡绩,我是刑警寧澤稽揭,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肥卡,受9級(jí)特大地震影響溪掀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜召调,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一膨桥、第九天 我趴在偏房一處隱蔽的房頂上張望蛮浑。 院中可真熱鬧唠叛,春花似錦只嚣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至障般,卻和暖如春调鲸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挽荡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工藐石, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人定拟。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓于微,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親青自。 傳聞我的和親對(duì)象是個(gè)殘疾皇子株依,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • 非原創(chuàng)文章,網(wǎng)絡(luò)收集延窜,如遇原作者恋腕,請(qǐng)私聊會(huì)標(biāo)明出處! 1--11 tcp協(xié)議中三次握手和四次揮手建立TCP需要三次...
    Juinjonn閱讀 2,162評(píng)論 0 28
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛(ài)閱讀 1,984評(píng)論 0 7
  • NIO(Non-blocking I/O逆瑞,在Java領(lǐng)域荠藤,也稱(chēng)為New I/O),是一種同步非阻塞的I/O模型获高,也...
    閃電是只貓閱讀 3,118評(píng)論 0 7
  • 那個(gè)時(shí)候并不知道映山紅就是杜鵑,也不知道望帝春心托杜鵑出爹,杜鵑啼血的故事庄吼。 只知道,清明節(jié)祭祖時(shí)映山紅就都開(kāi)了严就,一叢...
    林風(fēng)起閱讀 297評(píng)論 0 1
  • JS里面調(diào)用OC 對(duì)應(yīng)的Oc代碼為: 注意 在OC的實(shí)現(xiàn)中总寻,如果方法的參數(shù)需要使用float、int梢为、bool的渐行,...
    大熱天曬太陽(yáng)閱讀 1,340評(píng)論 0 0