nodejs深入學(xué)習(xí)系列之libuv基礎(chǔ)篇(一)

本文同步發(fā)表在豆米的博客

學(xué)習(xí)完nodejs基石之一的v8基礎(chǔ)篇(還沒看過的童鞋請?zhí)D(zhuǎn)到這里:nodejs深入學(xué)習(xí)系列之v8基礎(chǔ)篇)礁遵,我們這次將要繼續(xù)學(xué)習(xí)另外一塊基石:libuv。關(guān)于libuv的設(shè)計思想贪庙,我已經(jīng)翻譯成中文,還沒看過的童鞋還是請?zhí)D(zhuǎn)到這里: [譯文]libuv設(shè)計思想概述被芳,如果還沒看完這篇文章的童鞋烤送,下面的內(nèi)容也不建議細(xì)看了,因為會有”代溝“的問題~

本文的所有示例代碼都可以在這個倉庫中找到: libuv-demo

1爹土、libuv入門介紹

libuv是一個跨平臺聚焦于異步IO的庫,著名的event-loop便是libuv的一大特色踩身。我們要學(xué)習(xí)Libuv胀茵,那么就要先掌握libuv的編譯。

1.1挟阻、libuv的編譯簡單介紹

和v8一樣琼娘,libuv的編譯簡單概括如下:

  1. 先下載GYP:git clone https://chromium.googlesource.com/external/gyp build/gyp
  2. 指定ninja:./gyp_uv.py -f ninja
  3. 編譯:ninja -C out/Debug
  4. 跑測試: ./out/Debug/run-tests

1.2、libuv簡單使用

利用編譯好的libuv庫文件赁濒,我們可以開始寫一個簡單又經(jīng)典的例子: Hello world轨奄。

#include "stdio.h"
#include "uv.h"

int main() {
  uv_loop_t *loop = uv_default_loop();
  printf("hello libuv");
  uv_run(loop, UV_RUN_DEFAULT);
}

喜歡動手的童鞋可以下載一開始提到的demo,其中的hello_libuv.c便是拒炎,利用如何正確地使用v8嵌入到我們的C++應(yīng)用中這篇文章講到的運(yùn)行方式挪拟,我們借助CLion軟件和CMakeLists.txt文件來編譯所有的demo模塊,這方面就不再贅述了击你,記得將CMakeLists.txt文件中的include_directorieslink_directories改成你在第一小節(jié)編譯出來的Libuv靜態(tài)庫文件的目錄位置玉组。

好了,有了上面的基礎(chǔ)之后丁侄,我們開始結(jié)合demo來入門這個深藏眾多秘密的代碼庫惯雳。接下去的文章可能會比較長,一次讀不完的話建議收藏起來鸿摇,多讀幾次~

2石景、libuv的基礎(chǔ)概念介紹與實踐

看懂libuv之前,我們需要理解下面這些概念拙吉,并用實際用例來測試這些概念潮孽。

2.1、event-loop線程

我們都知道線程是操作系統(tǒng)最基本的調(diào)度單元筷黔,而進(jìn)程是操作系統(tǒng)的最基本的資源分配單元往史,因此可以知道進(jìn)程其實是不能運(yùn)行,能運(yùn)行的是進(jìn)程中的線程佛舱。進(jìn)程僅僅是一個容器椎例,包含了線程運(yùn)行中所需要的數(shù)據(jù)結(jié)構(gòu)等信息挨决。一個進(jìn)程創(chuàng)建時,操作系統(tǒng)會創(chuàng)建一個線程订歪,這就是主線程脖祈,而其他的從線程,都要在主線程的代碼來創(chuàng)建陌粹,也就是由程序員來創(chuàng)建撒犀。因此每一個可執(zhí)行的運(yùn)用程序都至少有一個線程

于是libuv一開始便啟動了event-loop線程,再在這個主線程上利用線程池去創(chuàng)建更多的線程掏秩。在event-loop線程中是一段while(1)的死循環(huán)代碼,直到?jīng)]有活躍的句柄的時候才會退出荆姆,這個時候libuv進(jìn)程才被銷毀掉蒙幻。清楚這點對于后面的學(xué)習(xí)至關(guān)重要。

2.2胆筒、Handle

中文翻譯為句柄邮破,如[譯文]libuv設(shè)計思想概述一文所屬,整個libuv的實現(xiàn)都是基于Handle和Request仆救。所以理解句柄以及l(fā)ibuv提供的所有句柄實例才能夠真的掌握libuv抒和。按照原文所述,句柄是:

表示能夠在活動時執(zhí)行某些操作的長生命周期對象彤蔽。

理解這句話的意思,首先我們抓住兩個關(guān)鍵詞:長生命周期摧莽、對象。Libuv所有的句柄都需要初始化顿痪,而初始化都會調(diào)用類似這種函數(shù):uv_xxx_init镊辕。xxx表示句柄的類型,在該函數(shù)中蚁袭,會將傳入的形參handle初始化征懈,并賦值返回具體的對象,比如初始化tcp句柄:

... // 隨便截取一段初始化代碼
handle->tcp.serv.accept_reqs = NULL;
handle->tcp.serv.pending_accepts = NULL;
handle->socket = INVALID_SOCKET;
handle->reqs_pending = 0;
handle->tcp.serv.func_acceptex = NULL;
handle->tcp.conn.func_connectex = NULL;
handle->tcp.serv.processed_accepts = 0;
handle->delayed_error = 0
...

理解了句柄其實就是個對象揩悄,那么長生命周期要是怎樣的卖哎?還是以TCP句柄為例子,你在這個例子tcpserver.c中删性,可以看到后面tcp服務(wù)器的操作:綁定端口亏娜、監(jiān)聽端口都是基于tcp句柄,整個句柄存活于整個應(yīng)用程序镇匀,只要tcp服務(wù)器沒有掛掉就一直在照藻,因此說是長生命周期的對象。

libuv提供的所有句柄如下:

image

接下去我們簡單介紹以下所有的Libuv的句柄

2.2.1汗侵、uv_handle_t

首先libuv有一個基本的handle, uv_handle_t幸缕,libuv是所有其他handle的基本范式群发,任何handle都可以強(qiáng)轉(zhuǎn)為該類型,并且和該Handle相關(guān)的所有API都可以為其他handle使用发乔。

libuv能否一直運(yùn)行下去的前提是檢查是否有活躍的句柄存在熟妓,而檢查一個句柄是否活躍(可以使用方法uv_is_active(const uv_handle_t* handle)檢查),根據(jù)句柄類型不同栏尚,其含義也不一樣:

  1. uv_async_t句柄總是活躍的并且不能停用起愈,除非使用uv_close關(guān)閉掉
  2. uv_pipe_tuv_tcp_t, uv_udp_t等译仗,這些牽扯到I/O的句柄一般也都是活躍
  3. uv_check_t, uv_idle_t, uv_timer_t等抬虽,當(dāng)這些句柄開始調(diào)用uv_check_start(), uv_idle_start()的時候也是活躍的。

而檢查哪些句柄活躍則可以使用這個方法:uv_print_active_handles(handle->loop, stderr);

tcpserver.c為例子纵菌,我們啟動tcp服務(wù)器后阐污,啟動一個定時器去打印存在的句柄,結(jié)果如下:

[-AI] async    0x10f78e9d8
[RA-] tcp      0x10f78e660
[RA-] timer    0x7ffee049d7c0

可以看到tcp的例子中一直存活的句柄是async咱圆、tcp笛辟、timer。它們前面中括號的標(biāo)志解釋如下:

R 表示該句柄被引用著
A 表示該句柄此時處于活躍狀態(tài)
I 表示該句柄是內(nèi)部使用的

2.2.2序苏、uv_timer_t

顧名思義手幢,Libuv的計時器,用來在將來某個時候調(diào)用對應(yīng)設(shè)置的回調(diào)函數(shù)忱详。其調(diào)用時機(jī)是在整個輪詢的最最開始围来,后面我們會說到輪詢的整個步驟。

2.2.3踱阿、uv_idle_t

Idle句柄在每次循環(huán)迭代中運(yùn)行一次給定的回調(diào)管钳,而且執(zhí)行順序是在prepare句柄之前。

prepare句柄的顯著區(qū)別在于软舌,當(dāng)存在活動的空閑句柄時才漆,循環(huán)將執(zhí)行零超時輪詢,而不是阻塞I/O佛点。

uv_backend_timeout方法中我們可以看到返回的輪詢I/O超時時間是0:

if (!QUEUE_EMPTY(&loop->idle_handles))
    return 0;

idle句柄的回調(diào)一般用來執(zhí)行一些低優(yōu)先級的任務(wù)醇滥。

**注意:盡管名稱叫做“idle”,空閑句柄在每次循環(huán)迭代時都會調(diào)用它們的回調(diào)函數(shù)超营,而不是在循環(huán)實際上是“空閑”的時候鸳玩。**

2.2.3、uv_prepare_t

prepare句柄將在每次循環(huán)迭代中運(yùn)行一次給定的回調(diào)演闭,而且是選擇在I/O輪詢之前不跟。

問題是:libuv為什么要創(chuàng)造這么一種句柄?其實從名稱來猜測米碰,libuv應(yīng)該是想提供一種方式讓你可以在輪詢I/O之前做些事情窝革,然后在輪詢I/O之后使用check句柄進(jìn)行一些結(jié)果的校驗购城。

2.2.4、uv_check_t

check句柄將在每次循環(huán)迭代中運(yùn)行一次給定的回調(diào)虐译,而且是選擇在I/O輪詢之后瘪板。其目的在上面已經(jīng)提過

2.2.5、uv_async_t

Async句柄允許用戶“喚醒”事件循環(huán)漆诽,并在主線程(原文翻譯為another thread侮攀,其實不對)調(diào)用一開始注冊的回調(diào)。這里說的喚醒其實就是發(fā)送消息給主線程(event-loop線程)厢拭,讓其可以執(zhí)行一開始注冊的回調(diào)了兰英。

**注意:libuv會對`uv_async_send()`做一個聚合處理。也就是說它并不會調(diào)用一次就執(zhí)行一次回調(diào)供鸠。**

我們使用thread.c為例子箭昵,使用uv_queue_workuv_async_send來實踐,得到的結(jié)果打印如下:

// 打印出主進(jìn)程ID號和event-loop線程ID
I am the master process, processId => 90714
I am event loop thread => 0x7fff8c2d9380

// 這個是uv_queue_work執(zhí)行的回調(diào)回季,從線程ID可以看到回調(diào)函數(shù)是在線程池中的某個線程中執(zhí)行
I am work callback, calling in some thread in thread pool, pid=>90714
work_cb thread id 0x700001266000

// 這個是uv_queue_work執(zhí)行完回調(diào)后結(jié)束的回調(diào),從線程ID可以看到這個回調(diào)已經(jīng)回到了主線程中執(zhí)行
I am after work callback, calling from event loop thread, pid=>90714
after_work_cb thread id 0x7fff8c2d9380

// 這個是uv_async_init的回調(diào)正林,其觸發(fā)是因為在work callback中執(zhí)行了uv_async_send泡一,可以從0x700001266000得到驗證,該回調(diào)也是在主線程中執(zhí)行
I am async callback, calling from event loop thread, pid=>90714
async_cb thread id 0x7fff8c2d9380
I am receiving msg: This msg from another thread: 0x700001266000

2.2.6觅廓、uv_poll_t

Poll句柄用于監(jiān)視文件描述符的可讀性鼻忠、可寫性和斷開連接,類似于poll(2)的目的杈绸。

Poll句柄的目的是支持集成外部庫帖蔓,這些庫依賴于事件循環(huán)來通知套接字狀態(tài)的更改,比如c-areslibssh2瞳脓。不建議將uv_poll_t用于任何其他目的;因為像uv_tcp_t塑娇、uv_udp_t等提供了一個比uv_poll_t更快、更可伸縮的實現(xiàn)劫侧,尤其是在Windows上埋酬。

可能輪詢處理偶爾會發(fā)出信號,表明文件描述符是可讀或可寫的烧栋,即使它不是写妥。因此,當(dāng)用戶試圖從fd讀取或?qū)懭霑r审姓,應(yīng)該總是準(zhǔn)備再次處理EAGAIN錯誤或類似的EAGAIN錯誤珍特。

同一個套接字不能有多個活躍的Poll句柄,因為這可能會導(dǎo)致libuv出現(xiàn)busyloop或其他故障魔吐。

當(dāng)活躍的Poll句柄輪詢文件描述符時扎筒,用戶不應(yīng)關(guān)閉該文件描述符莱找。否則可能導(dǎo)致句柄報告錯誤,但也可能開始輪詢另一個套接字砸琅。但是宋距,可以在調(diào)用uv_poll_stop()uv_close()之后立即安全地關(guān)閉fd。

**在Windows上症脂,只有套接字的文件描述符可以被輪詢谚赎,Linux上,任何[`poll(2)`](http://linux.die.net/man/2/poll)接受的文件描述符都可以被輪詢**

下面羅列的是輪詢的事件類型:

enum uv_poll_event {
    UV_READABLE = 1,
    UV_WRITABLE = 2,
    UV_DISCONNECT = 4,
    UV_PRIORITIZED = 8
};

2.2.7诱篷、uv_signal_t

Signal句柄在每個事件循環(huán)的基礎(chǔ)上實現(xiàn)Unix風(fēng)格的信號處理壶唤。在udpserver.c中展示了Signal句柄的使用方式:

uv_signal_t signal_handle;
r = uv_signal_init(loop, &signal_handle);
CHECK(r, "uv_signal_init");

r = uv_signal_start(&signal_handle, signal_cb, SIGINT);

void signal_cb(uv_signal_t *handle, int signum) {
  printf("signal_cb: recvd CTRL+C shutting down\n");
  uv_stop(uv_default_loop()); //stops the event loop
}

關(guān)于Signal句柄有幾個點要知悉:

  1. 以編程方式調(diào)用raise()abort()觸發(fā)的信號不會被libuv檢測到;所以這些信號不會對應(yīng)的回調(diào)函數(shù)。
  2. SIGKILL和SIGSTOP是不可能被捕捉到的
  3. 通過libuv處理SIGBUS棕所、SIGFPE闸盔、SIGILL或SIGSEGV會導(dǎo)致未定義的行為

2.2.8、uv_process_t

process句柄將會新建一個新的進(jìn)程并且能夠允許用戶控制該進(jìn)程并使用流去建立通信通道琳省。對應(yīng)的demo可以查看:process.c迎吵,值得注意的是,args中提供的結(jié)構(gòu)體的第一個參數(shù)path指的是可執(zhí)行程序的路徑针贬,比如在demo中:

const char* exepath = exepath_for_process();
char *args[3] = { (char*) exepath, NULL, NULL };

實例中的exepath是:FsHandle的執(zhí)行路徑击费。

另外一個注意點就是父子進(jìn)程的std的配置,demo中提供了一些參考桦他,如果使用管道的話還可以參考另外一個demo:pipe

2.2.9蔫巩、uv_stream_t

流句柄提供了雙工通信通道的抽象。uv_stream_t是一種抽象類型快压,libuv以uv_tcp_t圆仔、uv_pipe_tuv_tty_t的形式提供了3種流實現(xiàn)。這個沒有具體實例蔫劣。但是libuv有好幾個方法的入?yún)⒍际?code>uv_stream_t坪郭,說明這些方法都是可以被tcp/pipe/tty使用,具體有:

int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb)
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb)
int uv_accept(uv_stream_t* server, uv_stream_t* client)
int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb)
int uv_read_stop(uv_stream_t*)
int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb)
int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb)

2.2.10拦宣、uv_tcp_t

tcp句柄可以用來表示TCP流和服務(wù)器截粗。上小節(jié)說到的uv_stream_tuv_tcp_t的”父類“,這里使用結(jié)構(gòu)體繼承的方式實現(xiàn)鸵隧,uv_handle_t绸罗、uv_stream_tuv_tcp_t三者的結(jié)構(gòu)關(guān)系如下圖:

image

使用libuv創(chuàng)建tcp服務(wù)器的步驟可以歸納為:

1豆瘫、初始化uv_tcp_t: uv_tcp_init(loop, &tcp_server)
2珊蟀、綁定地址:uv_tcp_bind
3、監(jiān)聽連接:uv_listen
4、每當(dāng)有一個連接進(jìn)來之后育灸,調(diào)用uv_listen的回調(diào)腻窒,回調(diào)里要做如下事情:
  4.1、初始化客戶端的tcp句柄:uv_tcp_init()
  4.2磅崭、接收該客戶端的連接:uv_accept()
  4.3儿子、開始讀取客戶端請求的數(shù)據(jù):uv_read_start()
  4.4、讀取結(jié)束之后做對應(yīng)操作砸喻,如果需要響應(yīng)客戶端數(shù)據(jù)柔逼,調(diào)用uv_write,回寫數(shù)據(jù)即可割岛。

更多細(xì)節(jié)參考demo

2.2.11愉适、uv_pipe_t

Pipe句柄在Unix上提供了對本地域套接字的抽象,在Windows上提供了命名管道癣漆。它是uv_stream_t的“子類”维咸。管道的用途很多,可以用來讀寫文件惠爽,還可以用來做線程間的通信癌蓖。我們在實例中用來實現(xiàn)主線程與多個子線程的互相通信。實現(xiàn)的模型是這樣的:

image

從模型中可以看出婚肆,我們利用管道將客戶端的連接綁定到隨機(jī)的一個線程上费坊,之后的操作都是該線程和客戶端的通信。

2.2.12旬痹、uv_tty_t

TTY句柄表示控制臺的一種流,用的比較少讨越,就不多說了~

2.2.13两残、uv_udp_t

UDP句柄為客戶端和服務(wù)器封裝UDP通信。使用libuv創(chuàng)建udp服務(wù)器的步驟可以概括為:

1把跨、初始化接收端的uv_udp_t: uv_udp_init(loop, &receive_socket_handle)
2人弓、綁定地址:uv_udp_bind
3、開始接收消息:uv_udp_recv_start
4着逐、uv_udp_recv_start里執(zhí)行回調(diào)崔赌,可以使用下面方法回寫數(shù)據(jù)發(fā)送給客戶端
  4.1、uv_udp_init初始化send_socket_handle
  4.2耸别、uv_udp_bind綁定發(fā)送者的地址健芭,地址可以從recv獲取
  4.3、uv_udp_send發(fā)送指定消息

如果是官方文檔給出的示例的話秀姐,那么會使用uv_udp_set_broadcast設(shè)置廣播的地址慈迈。具體可以參考udp

2.2.14、uv_fs_event_t

FS事件句柄允許用戶監(jiān)視一個給定的路徑的更新事件省有,例如痒留,如果文件被重命名或其中有一個通用更改谴麦。這個句柄使用每個平臺上最佳的解決方案。

2.2.15伸头、uv_fs_poll_t

FS輪詢句柄允許用戶監(jiān)視給定的更改路徑匾效。與uv_fs_event_t不同,fs poll句柄使用stat檢測文件何時發(fā)生了更改恤磷,這樣它們就可以在不支持fs事件句柄的文件系統(tǒng)上工作面哼。

2.3、Request

那么接下去就說到Request這個短生命周期的概念碗殷,中文翻譯為”請求“精绎,類似于nodejs中的req,它也是一個結(jié)構(gòu)體。還是以上述的tcp服務(wù)器為例子锌妻,有這么一段代碼:

  if (r < 0) {
    // 如果接受連接失敗代乃,需要清理一些東西
    uv_shutdown_t *shutdown_req = malloc(sizeof(uv_shutdown_t));

    r = uv_shutdown(shutdown_req, (uv_stream_t *)tcp_client_handle, shutdown_cb);
    CHECK(r, "uv_shutdown");
  }

當(dāng)客戶端連接失敗炭菌,需要關(guān)閉掉這個連接颈渊,于是我們就會初始化一個request,然后傳遞給我們需要請求的操作寓娩,這里是關(guān)閉請求shutdown吭历。

關(guān)于libuv提供的句柄和request堕仔,我這里整理一張思維導(dǎo)圖,可以一看:

image

libuv的Request操作對比于句柄晌区,還是比較少的摩骨。上圖把每一個request的使用說明都講得一清二楚了。我們能做的就是隨時翻閱這篇文章即可朗若。

2.3.1恼五、uv_request_t

uv_request_t是基本的request,其他任何request都是基于該結(jié)構(gòu)進(jìn)行擴(kuò)展哭懈,它定義的所有api其他request都可以使用灾馒。和uv_handle_t一樣的功效。

2.4遣总、libuv運(yùn)行的三種模式

接著說說Libuv提供的三種運(yùn)行模式:

  • UV_RUN_DEFAULT 默認(rèn)輪詢模式睬罗,此模式會一直運(yùn)行事件循環(huán)直到?jīng)]有活躍句柄、引用句柄旭斥、和請求句柄
  • UV_RUN_ONCE 一次輪詢模式容达,此模式如果pending_queue中有回調(diào),則會執(zhí)行回調(diào)而直接跨過uv__io_poll垂券。如果沒有董饰,則此方式只會執(zhí)行一次I/O輪詢(uv__io_poll)。如果在執(zhí)行過后有回調(diào)壓入到了pending_queue中,則uv_run會返回非0卒暂,你需要在未來的某個時間再次觸發(fā)一次uv_run來清空pending_queue啄栓。
  • UV_RUN_NOWAIT 一次輪詢(無視pending_queue)模式,此模式類似UV_RUN_ONCE但是不會判斷pending_queue是否存在回調(diào)也祠,直接進(jìn)行一次I/O輪詢昙楚。

最后

ok,限于篇幅诈嘿,libuv的基礎(chǔ)篇仍未結(jié)束堪旧,你可以點我繼續(xù)閱讀第二篇,也可以先自己消化消化~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奖亚,一起剝皮案震驚了整個濱河市淳梦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昔字,老刑警劉巖爆袍,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異作郭,居然都是意外死亡陨囊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門夹攒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜘醋,“玉大人,你說我怎么就攤上這事咏尝⊙褂铮” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵编检,是天一觀的道長无蜂。 經(jīng)常有香客問我,道長蒙谓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任训桶,我火速辦了婚禮累驮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舵揭。我一直安慰自己谤专,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布午绳。 她就那樣靜靜地躺著置侍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜡坊,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天杠输,我揣著相機(jī)與錄音,去河邊找鬼秕衙。 笑死蠢甲,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的据忘。 我是一名探鬼主播鹦牛,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勇吊!你這毒婦竟也來了曼追?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤汉规,失蹤者是張志新(化名)和其女友劉穎礼殊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲫忍,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡膏燕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悟民。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坝辫。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖射亏,靈堂內(nèi)的尸體忽然破棺而出近忙,到底是詐尸還是另有隱情,我是刑警寧澤智润,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布及舍,位于F島的核電站,受9級特大地震影響窟绷,放射性物質(zhì)發(fā)生泄漏锯玛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一兼蜈、第九天 我趴在偏房一處隱蔽的房頂上張望攘残。 院中可真熱鬧,春花似錦为狸、人聲如沸歼郭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽病曾。三九已至牍蜂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泰涂,已是汗流浹背鲫竞。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留负敏,地道東北人贡茅。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像其做,于是被迫代替她去往敵國和親顶考。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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