epoll用于高并發(fā)網(wǎng)絡(luò)編程若锁,優(yōu)于select

在linux 沒(méi)有實(shí)現(xiàn)epoll事件驅(qū)動(dòng)機(jī)制之前凌净,我們一般選擇用select或者poll等IO多路復(fù)用的方法來(lái)實(shí)現(xiàn)并發(fā)服務(wù)程序悲龟。在大數(shù)據(jù)、高并發(fā)冰寻、集群等一些名詞唱得火熱之年代须教,select和poll的用武之地越來(lái)越有限,風(fēng)頭已經(jīng)被epoll占盡斩芭。

本文便來(lái)介紹epoll的實(shí)現(xiàn)機(jī)制轻腺,并附帶講解一下select和poll。通過(guò)對(duì)比其不同的實(shí)現(xiàn)機(jī)制秒旋,真正理解為何epoll能實(shí)現(xiàn)高并發(fā)约计。

select()和poll() IO多路復(fù)用模型

  • select的缺點(diǎn):
  1. 單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,通常是1024迁筛,當(dāng)然可以更改數(shù)量煤蚌,但由于select采用輪詢(xún)的方式掃描文件描述符,文件描述符數(shù)量越多细卧,性能越差尉桩;(在linux內(nèi)核頭文件posix_types.h中,有這樣的定義:#define __FD_SETSIZE 1024)贪庙。相比于select蜘犁,epoll最大的好處在于它不會(huì)隨著監(jiān)聽(tīng)fd數(shù)目的增長(zhǎng)而降低效率。
  2. 內(nèi)核 / 用戶(hù)空間內(nèi)存拷貝問(wèn)題止邮,select需要復(fù)制大量的句柄數(shù)據(jù)結(jié)構(gòu)这橙,產(chǎn)生巨大的開(kāi)銷(xiāo);
  3. select返回的是含有整個(gè)句柄的數(shù)組导披,應(yīng)用程序需要遍歷整個(gè)數(shù)組才能發(fā)現(xiàn)哪些句柄發(fā)生了事件屈扎;
  4. select的觸發(fā)方式是水平觸發(fā),應(yīng)用程序如果沒(méi)有完成對(duì)一個(gè)已經(jīng)就緒的文件描述符進(jìn)行IO操作撩匕,那么之后每次select調(diào)用還是會(huì)將這些文件描述符通知進(jìn)程鹰晨。

相比select模型,poll使用鏈表保存文件描述符止毕,因此沒(méi)有了監(jiān)視文件數(shù)量的限制模蜡,但其他三個(gè)缺點(diǎn)依然存在。

拿select模型為例扁凛,假設(shè)我們的服務(wù)器需要支持100萬(wàn)的并發(fā)連接忍疾,則在__FD_SETSIZE 為1024的情況下,則我們至少需要開(kāi)辟1k個(gè)進(jìn)程才能實(shí)現(xiàn)100萬(wàn)的并發(fā)連接谨朝。除了進(jìn)程間上下文切換的時(shí)間消耗外膝昆,從內(nèi)核/用戶(hù)空間大量的無(wú)腦內(nèi)存拷貝丸边、數(shù)組輪詢(xún)等叠必,是系統(tǒng)難以承受的荚孵。因此,基于select模型的服務(wù)器程序纬朝,要達(dá)到10萬(wàn)級(jí)別的并發(fā)訪(fǎng)問(wèn)收叶,是一個(gè)很難完成的任務(wù)。

因此共苛,該epoll上場(chǎng)了判没。

epoll IO多路復(fù)用模型實(shí)現(xiàn)機(jī)制

由于epoll的實(shí)現(xiàn)機(jī)制與select/poll機(jī)制完全不同,上面所說(shuō)的 select的缺點(diǎn)在epoll上不復(fù)存在隅茎。

設(shè)想一下如下場(chǎng)景:有100萬(wàn)個(gè)客戶(hù)端同時(shí)與一個(gè)服務(wù)器進(jìn)程保持著TCP連接澄峰。而每一時(shí)刻,通常只有幾百上千個(gè)TCP連接是活躍的(事實(shí)上大部分場(chǎng)景都是這種情況)辟犀。如何實(shí)現(xiàn)這樣的高并發(fā)俏竞?

在select/poll時(shí)代,服務(wù)器進(jìn)程每次都把這100萬(wàn)個(gè)連接告訴操作系統(tǒng)(從用戶(hù)態(tài)復(fù)制句柄數(shù)據(jù)結(jié)構(gòu)到內(nèi)核態(tài))堂竟,讓操作系統(tǒng)內(nèi)核去查詢(xún)這些套接字上是否有事件發(fā)生魂毁,輪詢(xún)完后,再將句柄數(shù)據(jù)復(fù)制到用戶(hù)態(tài)出嘹,讓服務(wù)器應(yīng)用程序輪詢(xún)處理已發(fā)生的網(wǎng)絡(luò)事件席楚,這一過(guò)程資源消耗較大,因此税稼,select/poll一般只能處理幾千的并發(fā)連接烦秩。

epoll的設(shè)計(jì)和實(shí)現(xiàn)與select完全不同。epoll通過(guò)在Linux內(nèi)核中申請(qǐng)一個(gè)簡(jiǎn)易的文件系統(tǒng)(文件系統(tǒng)一般用什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)郎仆?B+樹(shù))只祠。把原先的select/poll調(diào)用分成了3個(gè)部分:

  • 1)調(diào)用epoll_create()建立一個(gè)epoll對(duì)象

int epfd = epoll_create(int size);
創(chuàng)建一個(gè)epoll的句柄(epoll專(zhuān)用的文件描述符),size就是你在這個(gè)epoll fd上能關(guān)注的最大socket fd數(shù)丸升。這個(gè)參數(shù)不同于select()中的第一個(gè)參數(shù)铆农,給出最大監(jiān)聽(tīng)的fd+1的值(每次有個(gè)麻煩就是要先比較尋找到這個(gè)最大的fd)。需要注意的是狡耻,當(dāng)創(chuàng)建好epoll句柄后墩剖,它就是會(huì)占用一個(gè)fd值,在linux下如果查看/proc/進(jìn)程id/fd/夷狰,是能夠看到這個(gè)fd的岭皂,所以在使用完epoll后,必須調(diào)用close()關(guān)閉沼头,否則可能導(dǎo)致fd被耗盡爷绘。

  • 2)調(diào)用epoll_ctl向epoll對(duì)象中添加這100萬(wàn)個(gè)連接的套接字

函數(shù)聲明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
該函數(shù)用于控制某個(gè)epoll文件描述符上的事件书劝,可以注冊(cè)事件,修改事件土至,刪除事件购对。它不同與select()是在監(jiān)聽(tīng)事件時(shí)告訴內(nèi)核要監(jiān)聽(tīng)什么類(lèi)型的事件,而是在這里先注冊(cè)要監(jiān)聽(tīng)的事件類(lèi)型陶因。
參數(shù):
epfd:由 epoll_create 生成的epoll專(zhuān)用的文件描述符骡苞;
op:要進(jìn)行的操作例如注冊(cè)事件,可能的取值EPOLL_CTL_ADD 注冊(cè)楷扬、EPOLL_CTL_MOD 修 改解幽、EPOLL_CTL_DEL 刪除
fd:需要監(jiān)聽(tīng)的fd文件描述符。每調(diào)用一次這個(gè)函數(shù)烘苹,只操作一個(gè)fd躲株,所以如果要監(jiān)聽(tīng)很多個(gè)fd,也要循環(huán)這個(gè)函數(shù)添加所有的fd镣衡,這個(gè)與select類(lèi)似霜定。
event:指向epoll_event的指針,告訴內(nèi)核需要監(jiān)聽(tīng)什么事件;
如果調(diào)用成功返回0,不成功返回-1捆探。

  • structepoll_event結(jié)構(gòu)如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events可以是以下幾個(gè)宏的集合:

EPOLLIN: 觸發(fā)該事件然爆,表示對(duì)應(yīng)的文件描述符上有可讀數(shù)據(jù)。(包括對(duì)端SOCKET正常關(guān)閉)黍图;
EPOLLOUT:觸發(fā)該事件曾雕,表示對(duì)應(yīng)的文件描述符上可以寫(xiě)數(shù)據(jù);
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái))助被;
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤剖张;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET:將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式揩环,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的搔弄。
EPOLLONESHOT:只監(jiān)聽(tīng)一次事件,當(dāng)監(jiān)聽(tīng)完這次事件之后丰滑,如果還需要繼續(xù)監(jiān)聽(tīng)這個(gè)socket的話(huà)顾犹,
需要再次把這個(gè)socket加入到EPOLL隊(duì)列里。

編程實(shí)例:

struct epoll_event ev;
//設(shè)置與要處理的事件相關(guān)的文件描述符
ev.data.fd = listenfd;
//設(shè)置要處理的事件類(lèi)型
ev.events = EPOLLIN|EPOLLET;
//注冊(cè)epoll事件
epoll_ctl( epfd, EPOLL_CTL_ADD, listenfd, &ev);
  • 3)調(diào)用epoll_wait收集發(fā)生的事件的連接

如此一來(lái)褒墨,要實(shí)現(xiàn)上面說(shuō)是的場(chǎng)景炫刷,只需要在進(jìn)程啟動(dòng)時(shí)建立一個(gè)epoll對(duì)象,然后在需要的時(shí)候向這個(gè)epoll對(duì)象中添加或者刪除連接郁妈。同時(shí)浑玛,epoll_wait的效率也非常高,因?yàn)檎{(diào)用epoll_wait時(shí)噩咪,并沒(méi)有一股腦的向操作系統(tǒng)復(fù)制這100萬(wàn)個(gè)連接的句柄數(shù)據(jù)顾彰,內(nèi)核也不需要去遍歷全部的連接极阅。

  • 函數(shù)原型:int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    等待事件的產(chǎn)生,類(lèi)似于select()調(diào)用涨享。參數(shù)events用來(lái)從內(nèi)核得到事件的集合筋搏,maxevents告之內(nèi)核這個(gè)events有多大(數(shù)組成員的個(gè)數(shù)),這個(gè)maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size灰伟,參數(shù)timeout是超時(shí)時(shí)間(毫秒拆又,0會(huì)立即返回,-1將不確定栏账,也有說(shuō)法說(shuō)是永久阻塞)。
    該函數(shù)返回需要處理的事件數(shù)目栈源,如返回0表示已超時(shí)挡爵。
    返回的事件集合在events數(shù)組中,數(shù)組中實(shí)際存放的成員個(gè)數(shù)是函數(shù)的返回值甚垦。返回0表示已經(jīng)超時(shí)茶鹃。

epoll_wait運(yùn)行的原理是:
等待注冊(cè)在epfd上的socket fd的事件的發(fā)生,如果發(fā)生則將發(fā)生的sokct fd和事件類(lèi)型放入到events數(shù)組中艰亮。
并 且將注冊(cè)在epfd上的socket fd的事件類(lèi)型給清空闭翩,所以如果下一個(gè)循環(huán)你還要關(guān)注這個(gè)socket fd的話(huà),則需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)來(lái)重新設(shè)置socket fd的事件類(lèi)型迄埃。這時(shí)不用EPOLL_CTL_ADD,因?yàn)閟ocket fd并未清空疗韵,只是事件類(lèi)型清空。這一步非常重要侄非。

下面來(lái)看一個(gè)服務(wù)器實(shí)例

  • 關(guān)于ET蕉汪、LT兩種工作模式,可以得出這樣的結(jié)論:
    ET模式僅當(dāng)狀態(tài)發(fā)生變化的時(shí)候才獲得通知,這里所謂的狀態(tài)的變化并不包括緩沖區(qū)中還有未處理的數(shù)據(jù),也就是說(shuō),如果要采用ET模式,需要一直read/write直到出錯(cuò)為止,很多人反映為什么采用ET模式只接收了一部分?jǐn)?shù)據(jù)就再也得不到通知了,大多因?yàn)檫@樣;而LT模式是只要有數(shù)據(jù)沒(méi)有處理就會(huì)一直通知下去的.

  • 那么究竟如何來(lái)使用epoll呢?其實(shí)非常簡(jiǎn)單逞怨。
    通過(guò)在包含一個(gè)頭文件#include <sys/epoll.h> 以及幾個(gè)簡(jiǎn)單的API將可以大大的提高你的網(wǎng)絡(luò)服務(wù)器的支持人數(shù)者疤。

  • 首先通過(guò)create_epoll(int maxfds)來(lái)創(chuàng)建一個(gè)epoll的句柄,其中maxfds為你epoll所支持的最大句柄數(shù)叠赦。這個(gè)函數(shù)會(huì)返回一個(gè)新的epoll句柄驹马,之后的所有操作將通過(guò)這個(gè)句柄來(lái)進(jìn)行操作。在用完之后除秀,記得用close()來(lái)關(guān)閉這個(gè)創(chuàng)建出來(lái)的epoll句柄糯累。

  • 之后在你的網(wǎng)絡(luò)主循環(huán)里面,每一幀的調(diào)用epoll_wait(int epfd, epoll_event events, int max events, int timeout)來(lái)查詢(xún)所有的網(wǎng)絡(luò)接口鳞仙,看哪一個(gè)可以讀寇蚊,哪一個(gè)可以寫(xiě)了」骱茫基本的語(yǔ)法為:
    nfds = epoll_wait(epfd, events, maxevents, -1);
    其中epfd為用epoll_create創(chuàng)建之后的句柄仗岸,events是一個(gè)epoll_event*的指針允耿,當(dāng)epoll_wait這個(gè)函數(shù)操作成功之后,epoll_events里面將儲(chǔ)存所有的讀寫(xiě)事件扒怖。max_events是當(dāng)前需要監(jiān)聽(tīng)的所有socket句柄數(shù)较锡。最后一個(gè)timeout是 epoll_wait的超時(shí),為0的時(shí)候表示馬上返回盗痒,為-1的時(shí)候表示一直等下去蚂蕴,直到有事件范圍,為任意正整數(shù)的時(shí)候表示等這么長(zhǎng)的時(shí)間俯邓,如果一直沒(méi)有事件骡楼,則返回。一般如果網(wǎng)絡(luò)主循環(huán)是單獨(dú)的線(xiàn)程的話(huà)稽鞭,可以用-1來(lái)等鸟整,這樣可以保證一些效率,如果是和主邏輯在同一個(gè)線(xiàn)程的話(huà)朦蕴,則可以用0來(lái)保證主循環(huán)的效率篮条。

epoll_wait調(diào)用返回之后應(yīng)該是一個(gè)循環(huán),遍利所有的事件吩抓。
幾乎所有的epoll程序都使用下面的框架:(與select函數(shù)調(diào)用返回后思路一致)

    for( ; ; )
    {
        nfds = epoll_wait(epfd,events,20,500);
        for(i=0;i<nfds;++i)
        {
            if(events[i].data.fd==listenfd) //有新的連接
            {
                connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept這個(gè)連接
                ev.data.fd=connfd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //將新的fd添加到epoll的監(jiān)聽(tīng)隊(duì)列中
            }
            else if( events[i].events&EPOLLIN ) //接收到數(shù)據(jù)涉茧,讀socket
            {
                n = read(sockfd, line, MAXLINE)) < 0    //讀
                ev.data.ptr = md;     //md為自定義類(lèi)型,添加數(shù)據(jù)
                ev.events=EPOLLOUT|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改標(biāo)識(shí)符疹娶,等待下一個(gè)循環(huán)時(shí)發(fā)送數(shù)據(jù)伴栓,異步處理的精髓
            }
            else if(events[i].events&EPOLLOUT) //有數(shù)據(jù)待發(fā)送,寫(xiě)socket
            {
                struct myepoll_data* md = (myepoll_data*)events[i].data.ptr;    //取數(shù)據(jù)
                sockfd = md->fd;
                send( sockfd, md->ptr, strlen((char*)md->ptr), 0 );        //發(fā)送數(shù)據(jù)
                ev.data.fd=sockfd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改標(biāo)識(shí)符蚓胸,等待下一個(gè)循環(huán)時(shí)接收數(shù)據(jù)
            }
            else
            {
                //其他的處理
            }
        }
    }

下面給出一個(gè)完整的服務(wù)器端例子:

#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

using namespace std;

#define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000

void setnonblocking(int sock)
{
    int opts;
    opts=fcntl(sock,F_GETFL);
    if(opts<0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts|O_NONBLOCK;
    if(fcntl(sock,F_SETFL,opts)<0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}

int main(int argc, char* argv[])
{
    int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber;
    ssize_t n;
    char line[MAXLINE];
    socklen_t clilen;


    if ( 2 == argc )
    {
        if( (portnumber = atoi(argv[1])) < 0 )
        {
            fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]);
            return 1;
        }
    }
    else
    {
        fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]);
        return 1;
    }



    //聲明epoll_event結(jié)構(gòu)體的變量,ev用于注冊(cè)事件,數(shù)組用于回傳要處理的事件

    struct epoll_event ev,events[20];
    //生成用于處理accept的epoll專(zhuān)用的文件描述符

    epfd=epoll_create(256);
    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    //把socket設(shè)置為非阻塞方式

    //setnonblocking(listenfd);

    //設(shè)置與要處理的事件相關(guān)的文件描述符

    ev.data.fd=listenfd;
    //設(shè)置要處理的事件類(lèi)型

    ev.events=EPOLLIN|EPOLLET;
    //ev.events=EPOLLIN;

    //注冊(cè)epoll事件

    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    char *local_addr="127.0.0.1";
    inet_aton(local_addr,&(serveraddr.sin_addr));//htons(portnumber);

    serveraddr.sin_port=htons(portnumber);
    bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
    listen(listenfd, LISTENQ);
    maxi = 0;
    for ( ; ; ) {
        //等待epoll事件的發(fā)生

        nfds=epoll_wait(epfd,events,20,500);
        //處理所發(fā)生的所有事件

        for(i=0;i<nfds;++i)
        {
            if(events[i].data.fd==listenfd)
            {
                //如果新監(jiān)測(cè)到一個(gè)SOCKET用戶(hù)連接到了綁定的SOCKET端口挣饥,建立新的連接。
                connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
                if(connfd<0){
                    perror("connfd<0");
                    exit(1);
                }
                //setnonblocking(connfd);

                char *str = inet_ntoa(clientaddr.sin_addr);
                cout << "accapt a connection from " << str << endl;
                //設(shè)置用于讀操作的文件描述符

                ev.data.fd=connfd;
                //設(shè)置用于注測(cè)的讀操作事件

                ev.events=EPOLLIN|EPOLLET;
                //ev.events=EPOLLIN;

                //注冊(cè)ev

                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
            }
            else if(events[i].events&EPOLLIN)

            {
                //如果是已經(jīng)連接的用戶(hù)沛膳,并且收到數(shù)據(jù)扔枫,那么進(jìn)行讀入。
                cout << "EPOLLIN" << endl;
                if ( (sockfd = events[i].data.fd) < 0)
                    continue;
                if ( (n = read(sockfd, line, MAXLINE)) < 0) {
                    if (errno == ECONNRESET) {
                        close(sockfd);
                        events[i].data.fd = -1;
                    } else
                        std::cout<<"readline error"<<std::endl;
                } else if (n == 0) {
                    close(sockfd);
                    events[i].data.fd = -1;
                }
                line[n] = '/0';
                cout << "read " << line << endl;
                //設(shè)置用于寫(xiě)操作的文件描述符

                ev.data.fd=sockfd;
                //設(shè)置用于注測(cè)的寫(xiě)操作事件

                ev.events=EPOLLOUT|EPOLLET;
                //修改sockfd上要處理的事件為EPOLLOUT

                //epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

            }
            else if(events[i].events&EPOLLOUT) // 如果有數(shù)據(jù)發(fā)送

            {
                sockfd = events[i].data.fd;
                write(sockfd, line, n);
                //設(shè)置用于讀操作的文件描述符

                ev.data.fd=sockfd;
                //設(shè)置用于注測(cè)的讀操作事件

                ev.events=EPOLLIN|EPOLLET;
                //修改sockfd上要處理的事件為EPOLIN

                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
            }
        }
    }
    return 0;
}

客戶(hù)端直接連接到這個(gè)服務(wù)器就好了锹安。短荐。

https://blog.csdn.net/ljx0305/article/details/4065058
https://blog.csdn.net/shenya1314/article/details/73691088
https://blog.csdn.net/yusiguyuan/article/details/15027821

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叹哭,隨后出現(xiàn)的幾起案子忍宋,更是在濱河造成了極大的恐慌,老刑警劉巖风罩,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糠排,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡超升,警方通過(guò)查閱死者的電腦和手機(jī)入宦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門(mén)哺徊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人乾闰,你說(shuō)我怎么就攤上這事落追。” “怎么了涯肩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵轿钠,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我病苗,道長(zhǎng)疗垛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任铅乡,我火速辦了婚禮继谚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阵幸。我一直安慰自己,他們只是感情好芽世,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布挚赊。 她就那樣靜靜地躺著,像睡著了一般济瓢。 火紅的嫁衣襯著肌膚如雪荠割。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,874評(píng)論 1 314
  • 那天旺矾,我揣著相機(jī)與錄音蔑鹦,去河邊找鬼。 笑死箕宙,一個(gè)胖子當(dāng)著我的面吹牛嚎朽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柬帕,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哟忍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了陷寝?” 一聲冷哼從身側(cè)響起锅很,我...
    開(kāi)封第一講書(shū)人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凤跑,沒(méi)想到半個(gè)月后爆安,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仔引,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年扔仓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褐奥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡当辐,死狀恐怖抖僵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缘揪,我是刑警寧澤耍群,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站找筝,受9級(jí)特大地震影響蹈垢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袖裕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一曹抬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧急鳄,春花似錦谤民、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至坎藐,卻和暖如春为牍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背岩馍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工碉咆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛀恩。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓疫铜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赦肋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子块攒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

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