linux 多路復(fù)用io,select 和 epoll 函數(shù)的tcp應(yīng)用場景總結(jié)

以下內(nèi)容部分經(jīng)過驗(yàn)證草讶,可能有些地方需要讀者按照例子自己驗(yàn)證

一祭椰、linux 網(wǎng)絡(luò)io相關(guān)函數(shù)

linux 關(guān)于io(包括網(wǎng)絡(luò)io)的操作泥张,都抽象成是面向文件的模型莹痢。

1种蘸、文件描述符

linux下文件描述符是int的整數(shù)值,前兩個被系統(tǒng)占用(終端竞膳,和異常)航瞭。
關(guān)于網(wǎng)絡(luò)套接字的io,服務(wù)端和客戶端坦辟,第一步都是調(diào)用socket函數(shù)創(chuàng)建 ‘套接字’刊侯,返回一個文件‘描述符fd’。也可以理解為創(chuàng)建了一個文件锉走。
服務(wù)端的這個‘文件描述符’滨彻,用于接收連接請求,通過accept函數(shù)挪蹭。
客戶端的這個‘文件描述符’ 亭饵,用于客戶端向服務(wù)端發(fā)送和接收數(shù)據(jù),通過read/recv和write/send函數(shù) 梁厉。

(1)辜羊、創(chuàng)建網(wǎng)絡(luò)傳輸根文件描述符:socket函數(shù)

int socket(int domain, int type, int protocol); 返回的描述符,默認(rèn)是阻塞模式词顾,若type參數(shù)傳入 (xxx |SOCK_NONBLOCK) 就是非阻塞的八秃,也可以用文件描述符通過fcntl() 函數(shù)或 ioctl() 函數(shù),將套接字設(shè)置成非阻塞的肉盹。

舉例1:
socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0)

舉例2:
int flags = fcntl('文件描述符', F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl('文件描述符', F_SETFL, flags);

(2)昔驱、服務(wù)端開啟監(jiān)聽函數(shù): listen('文件描述符', 'tcp 連接隊(duì)列長度');

關(guān)于 listen 函數(shù)的最后一個參數(shù),不同的linux版本上忍,代表不同的含義舍悯。在Linux內(nèi)核2.2之后航棱,listen的最后一個參數(shù)(socket backlog)的形為是指等待accept的完全建立(tcp狀態(tài)ESTABLISHED )的套接字的隊(duì)列長度,不包括不完全(tcp 狀態(tài) SYN RECEIVED )連接請求的數(shù)量萌衬。 不完全連接的長度可以使用系統(tǒng)參數(shù) /proc/sys/net/ipv4/tcp_max_syn_backlog設(shè)置饮醇。

(3)、服務(wù)端接受連接: accept函數(shù)

accept('socket 函數(shù)返回的描述符', '出參: 客戶端ip地址', '地址長度')秕豫,返回值:服務(wù)器端為客戶端創(chuàng)建的文件描述符朴艰。服務(wù)端向客戶端發(fā)送和接受數(shù)據(jù)是使用。是否為阻塞的和'socket 函數(shù)返回的描述符'一致混移,也可以用accept4函數(shù)返回的文件描述符為非阻塞的祠墅。或通過fcntl() 函數(shù)設(shè)置為非阻塞的歌径。

舉例1:
int accept4(sockfd,(struct sockaddr*)&cli,&len,SOCK_NONBLOCK);

(4)毁嗦、客戶端主動連接服務(wù)端: connect 函數(shù)

次函數(shù)會經(jīng)過tcp三次握手,若sockfd文件操作符回铛,非阻塞方式不等三次握手完成的終止態(tài)ESTABLISHED,connect 的函數(shù)就會返回小于0的值狗准。

   if((connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)))<0){
        printf("tcp 三次握手未完成,或錯誤無!");
        //exit(1);
   }

2茵肃、socket讀寫操作

(1)腔长、讀操作

linux 為讀操作提供了:read(文件描述符, 緩沖, 緩沖長度)、recv(文件描述符, 緩沖, 緩沖長度,讀取類型) 函數(shù)验残。
read函數(shù):如果入?yún)ⅰ募枋龇?是阻塞的捞附,即為阻塞模式讀取。如果入?yún)ⅰ募枋龇?是非阻塞的您没,就以非阻塞方式讀取鸟召。
recv函數(shù):同read函數(shù)類似,區(qū)別是‘讀取類型’ 參數(shù)氨鹏。決定是否為阻塞讀取

 舉例1:
 recv(sockfd, buff, buff_size,MSG_DONTWAIT);  

入?yún)ⅲ?br> MSG_DONTWAIT:這個標(biāo)志將單個IO操作設(shè)為非堵塞方式药版,而不需要在套接字上打開非堵塞的標(biāo)志,執(zhí)行IO操作喻犁。然后關(guān)閉非堵塞的標(biāo)志槽片。
MSG_WAITALL:這個標(biāo)志告訴內(nèi)核在沒有讀到請求的字節(jié)數(shù)之前不使讀操作返回。
注意:
使用MSG_WAITALL時肢础,’文件描述符‘ 必須處于阻塞模式下还栓,否則不起作用。所MSG_WAITALL不能和MSG_NONBLOCK同時使用传轰。

返回值: 成功執(zhí)行時剩盒,返回接收到的字節(jié)數(shù)。另一端已關(guān)閉則返回0慨蛙。失敗返回-1辽聊,errno被設(shè)為以下的某個值
EAGAIN:套接字已標(biāo)記為非阻塞纪挎,而接收操作被阻塞或者接收超時EBADF:sock不是有效的描述詞
ECONNREFUSE:遠(yuǎn)程主機(jī)阻絕網(wǎng)絡(luò)連接
EFAULT:內(nèi)存空間訪問出錯
EINTR:操作被信號中斷
EINVAL:參數(shù)無效
ENOMEM:內(nèi)存不足
ENOTCONN:與面向連接關(guān)聯(lián)的套接字尚未被連接上
ENOTSOCK:sock索引的不是套接字

(2)、linux 為寫操作提供了:write跟匆、send函數(shù)
(3) 讀寫函數(shù)總結(jié):
  • 1> send 函數(shù):并不是往網(wǎng)絡(luò)上發(fā)送數(shù)據(jù)异袄,而是將應(yīng)用層發(fā)送緩沖區(qū)的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)(網(wǎng)卡緩沖區(qū))中去,至于什么時候數(shù)據(jù)會從網(wǎng)卡緩沖區(qū)中真正地發(fā)到網(wǎng)絡(luò)中去要根據(jù) TCP/IP 協(xié)議棧的行為來確定玛臂,這種行為涉及到一個叫 nagel 算法和 TCP_NODELAY 的 socket 選項(xiàng)烤蜕。

  • 2> recv 函數(shù):不是從網(wǎng)絡(luò)上收取數(shù)據(jù),而只是將內(nèi)核緩沖區(qū)中的數(shù)據(jù)拷貝到應(yīng)用程序的緩沖區(qū)中迹冤,拷貝完成以后會將內(nèi)核緩沖區(qū)中該部分?jǐn)?shù)據(jù)移除讽营。

二、linux socket阻塞和非阻塞有哪些不同(引用)

  1. 建立連接
    阻塞方式下泡徙,客戶端 connect首先發(fā)送SYN請求到服務(wù)器橱鹏,當(dāng)客戶端收到服務(wù)器返回的SYN的確認(rèn)時,則connect返回堪藐,否則的話一直阻塞莉兰。
    非阻塞方式,connect將啟用TCP協(xié)議的三次握手庶橱,但是connect函數(shù)并不等待連接建立好才返回贮勃,而是立即返回贪惹,返回的錯誤碼為EINPROGRESS,表示正在進(jìn)行某種過程苏章。
  2. 接收連接
    阻塞模式下調(diào)用accept()函數(shù),而且沒有新連接時奏瞬,進(jìn)程會進(jìn)入睡眠狀態(tài)枫绅,直到有可用的連接,才返回硼端。
    非阻塞模式下調(diào)用accept()函數(shù)立即返回并淋,有連接返回客戶端套接字描述符,沒有新連接時珍昨,將返回EWOULDBLOCK錯誤碼县耽,表示本來應(yīng)該阻塞。
  3. 讀操作
    阻塞模式下調(diào)用read(),recv()等讀套接字函數(shù)會一直阻塞住镣典,直到有數(shù)據(jù)到來才返回兔毙。當(dāng)socket緩沖區(qū)中的數(shù)據(jù)量小于期望讀取的數(shù)據(jù)量時,返回實(shí)際讀取的字節(jié)數(shù)兄春。當(dāng)sockt的接收緩沖區(qū)中的數(shù)據(jù)大于期望讀取的字節(jié)數(shù)時澎剥,讀取期望讀取的字節(jié)數(shù),返回實(shí)際讀取的長度赶舆。
    對于非阻塞socket而言哑姚,socket的接收緩沖區(qū)中有沒有數(shù)據(jù)祭饭,read調(diào)用都會立刻返回。接收緩沖區(qū)中有數(shù)據(jù)時叙量,與阻塞socket有數(shù)據(jù)的情況是一樣的倡蝙,如果接收緩沖區(qū)中沒有數(shù)據(jù),則返回錯誤號為EWOULDBLOCK宛乃,表示該操作本來應(yīng)該阻塞的悠咱,但是由于本socket為非阻塞的socket,因此立刻返回征炼。遇到這樣的情況析既,可以在下次接著去嘗試讀取。如果返回值是其它負(fù)值谆奥,則表明讀取錯誤眼坏。
  4. 寫操作
    對于阻塞Socket而言,如果發(fā)送緩沖區(qū)沒有空間或者空間不足的話酸些,write操作會直接阻塞住宰译,如果有足夠空間,則拷貝所有數(shù)據(jù)到發(fā)送緩沖區(qū)魄懂,然后返回.
    對于寫操作write,原理和read是類似的沿侈,非阻塞socket在發(fā)送緩沖區(qū)沒有空間時會直接返回錯誤號EWOULDBLOCK,表示沒有空間可寫數(shù)據(jù),如果錯誤號是別的值市栗,則表明發(fā)送失敗缀拭。如果發(fā)送緩沖區(qū)中有足夠空間或者是不足以拷貝所有待發(fā)送數(shù)據(jù)的空間的話,則拷貝前面N個能夠容納的數(shù)據(jù)填帽,返回實(shí)際拷貝的字節(jié)數(shù)蛛淋。
    尤其注意非阻塞的socket,在建立連接時要兼容處理返回EINPROGRESS情況篡腌,在接收連接褐荷、讀操作、寫操作時要兼容處理返回EWOULDBLOCK錯誤碼的情況嘹悼。

三叛甫、linux select 方法實(shí)現(xiàn)的服務(wù)端

//服務(wù)端程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>//hotn
#include <unistd.h>
#include <sys/select.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
//文件描述符(file descriptor)是內(nèi)核為文件所創(chuàng)建的索引
//Linux剛啟動的時候會自動設(shè)置0是標(biāo)準(zhǔn)輸入,1是標(biāo)準(zhǔn)輸出杨伙,2是標(biāo)準(zhǔn)錯誤其监。
int main(int argc, char const *argv[])
{
    //判斷入?yún)?    if (argc<2){
        printf("eg: ./a.out prot\n");
        exit(1);
    }
    //atoi 類型轉(zhuǎn)換
    int port = atoi(argv[1]);
    //開啟服務(wù)端 socket文件描述符,用戶標(biāo)記新連接的接收
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    //初始化服務(wù)器 sockaddr_in
    struct sockaddr_in serverAddr;
    socklen_t serverAddrLen= sizeof(serverAddr);
    memset(&serverAddr,0,serverAddrLen);
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(port);
    //將文件描述符綁定端口和地址
    bind(lfd, (struct sockaddr *)&serverAddr, serverAddrLen);
    //開啟監(jiān)聽,設(shè)置最大
    listen(lfd, 3);
    printf("select io:Start accept .....\n");
    //客戶端地址 聲明
    struct sockaddr_in clientAddr;
    socklen_t clientAddrLen= sizeof(clientAddr);

    //-------------- select 相關(guān)代碼----------------------------------------
    int MAX_SOCK_FD_INDEX = 12;
    //超時時間
    struct timeval timeout;
    timeout.tv_sec = 10;
    timeout.tv_usec = 1000;
    //可讀取的文件描述符集合
    fd_set readFds;
    //初始文件描述符'標(biāo)記'集合 is_connected 數(shù)組的index為文件描述符‘索引’
    int isConnected[MAX_SOCK_FD_INDEX];
    for(int i = 0; i < MAX_SOCK_FD_INDEX; i++)
        isConnected[i] = 0;

    while(1){
        //將讀操作集合重置
        FD_ZERO(&readFds);
        //將服務(wù)端描述符缀台,設(shè)置為可讀操作
        FD_SET(lfd, &readFds);
        //將準(zhǔn)備就緒的棠赛,文件描述,設(shè)置為可讀操作
        for(int i= 0; i < MAX_SOCK_FD_INDEX; i++)
            if(isConnected[i])
                FD_SET(i, &readFds);

        if(!select(MAX_SOCK_FD_INDEX, &readFds, NULL, NULL, &timeout))
            //如果超時那么跳過循環(huán)
            continue;
        //循環(huán)所有監(jiān)聽的描述符
        for(int i = 0; i < MAX_SOCK_FD_INDEX; i++){
            //如果文件描述符是可讀的
            if(FD_ISSET(i, &readFds)){
                int fd = i;
                //如果可讀的文件描述符為 '根文件描述符' ,那么說明是-----新的連接
                if(lfd == fd){
                    int cfd = accept(lfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
                    if(cfd ==-1){
                        perror("accpet error");
                        exit(0);
                    }
                    //向新文件描述符(連接)寫入數(shù)據(jù)
                    //write(cfd, "hello world", sizeof("hello world"));
                    //將新文件描述符設(shè)置為 可讀
                    isConnected[cfd] = 1;
                    //打印客戶端地址
                    char ip[64] = {0};
                    printf("new Clinet ip %s, Port %d \n",
                        inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,ip,sizeof(ip)),
                        ntohs(clientAddr.sin_port)
                    );
                }
                //否則為 久文件描述符睛约,那么是可讀事件
                else{
                    char buf[256];
                    bzero(buf, sizeof(buf));
                    //從文件描述符鼎俘,讀取信息, 如果沒有讀到數(shù)據(jù)說明辩涝,連接斷了
                    if(read(fd, buf , sizeof(buf)) <= 0){
                        printf("Connection closed.\n");
                        //取消文件描述符的可讀狀態(tài)
                        isConnected[fd] = 0;
                        //關(guān)閉文件描述符
                        close(fd);
                    }
                    else{
                        //打印客戶端輸入的內(nèi)容
                        printf("%s", buf);
                    }
                }
            }
        }
    }
}

四贸伐、linux epoll 方法實(shí)現(xiàn)的服務(wù)端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>//hotn
#include <unistd.h>
#include <sys/epoll.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    if (argc<2) {
        printf("eg: ./a.out prot\n");
        exit(1);
    }
    //atoi 類型轉(zhuǎn)換
    int port = atoi(argv[1]);

    //創(chuàng)建套接字,文件描述符怔揩,接受連接的節(jié)點(diǎn)
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    //服務(wù)端地址
    struct sockaddr_in serverAddr;
    socklen_t serverAddrlen= sizeof(serverAddr);
    //初始化服務(wù)器 sockaddr_in
    memset(&serverAddr,0,serv_len);
    //地址族
    serverAddr.sin_family=AF_INET;
    //設(shè)置監(jiān)聽本機(jī)ip
    serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    //設(shè)置監(jiān)聽端口
    serverAddr.sin_port=htons(port);
    //綁定ip
    bind(lfd,(struct sockaddr *)&serverAddr,serverAddrlen);
    
    //監(jiān)聽 最大值128
    //在Linux內(nèi)核2.2之后捉邢,socket backlog參數(shù)(listen的最后一個參數(shù))的形為改變了,
    //現(xiàn)在它指等待accept的完全建立(ESTABLISHED 狀態(tài))的套接字的隊(duì)列長度商膊,而不是不完全(SYN RECEIVED 狀態(tài))連接請求的數(shù)量伏伐。 
    //不完全連接的長度可以使用/proc/sys/net/ipv4/tcp_max_syn_backlog設(shè)置。
    listen(lfd,128);
    printf("epoll io:Start accept .....\n");
    //客戶端地址 聲明
    struct sockaddr_in clientAddr;
    socklen_t clientAddrLen= sizeof(clientAddr);

    //-------------- epoll 相關(guān)代碼----------------------------------------
    //創(chuàng)建epoll樹, 初始創(chuàng)建2000個子節(jié)點(diǎn)晕拆, 可擴(kuò)容
    int epfd = epoll_create(2000);
    //初始化藐翎,根節(jié)點(diǎn) 關(guān)注的事件
    struct epoll_event ev;
    //讀寫事件
    ev.events=EPOLLIN;
    ev.data.fd=lfd;
    //初始化epoll數(shù)根節(jié)點(diǎn)
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    //聲明 內(nèi)核返回的,檢測到的事件數(shù)組
    struct epoll_event all[2000];
    while(1){
        //使用epoll 通知內(nèi)核 文件io檢測,第3參數(shù)為數(shù)組大小,最后一個參數(shù)-1代表阻塞
        //返回值為实幕,有多少個元素發(fā)生了吝镣,io事件
        int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(0),-1);
        for (int i = 0; i < ret; ++i) {
            int fd=all[i].data.fd;
            if(fd == lfd){//新連接
                //接受連接請求
                int cfd=accept(lfd,(struct sockaddr *)&clientAddr, &clientAddrLen);
                if(cfd ==-1){
                    perror("accpet error");
                    exit(0);
                }
                //初始化,普通節(jié)點(diǎn) 關(guān)注的事件
                struct epoll_event tmp;
                //讀寫事件
                tmp.events=EPOLLIN;
                tmp.data.fd=cfd;
                //將新得到的cfd掛在樹上
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&tmp);
                //打印客戶端信息
                char ip[64] = {0};
                printf("new Clinet ip %s, Port %d \n",
                    inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,ip,sizeof(ip)),
                    ntohs(clientAddr.sin_port)
                );
            }else {
                //處理已連接的客戶端發(fā)來的數(shù)據(jù)
                char buf[1024] ={0};
                int len = recv(fd, buf, sizeof(buf), 0);
                if(len == -1){
                    perror("recv error");
                }else if(len == 0){
                    printf("clent close");
                    close(fd);
                    //將fd從樹上刪除
                    epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
                }else{
                    printf("recv buf %s\n", buf);
                    //將 buf 回寫客戶端
                    write(fd,buf,len);
                }
            }
        }
    }
    close(lfd);
    return 0;
}

五昆庇、linux 客戶端


//客戶端程序
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <ctype.h>
// socket編程中write末贾、read和send、recv之間的區(qū)別 , 在于第四個參數(shù)recv可以控制是否整吆,讀取完成后是否刪除緩沖
void main(int argc, char const *argv[]){
    //判斷入?yún)?    if (argc<2 || argc>3){
        printf("eg: ./a.out [ip] prot\n");
        exit(1);
    }
    const char *ip="127.0.0.1";
    int port;
    if (argc==3){
        ip = argv[1];
        port = atoi(argv[2]);
    }else{
        port = atoi(argv[1]);
    }

    //連接地址
    struct sockaddr_in addr;
    //創(chuàng)建根文件描述符
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    bzero(&addr,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);
    //創(chuàng)建連接拱撵,此處三次握手,如果socket 創(chuàng)建時掂为,參數(shù)為非阻塞裕膀,那么此處不等三次握手完成员串,立即返回勇哗。
    if((connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)))<0){
        printf("tcp 三次握手未完成,或錯誤無!");
        exit(1);
    }
    //讀取數(shù)據(jù)的大小
    char buf[256];
    //從根文件描述符讀取數(shù)據(jù)寸齐,最后一個參數(shù)可設(shè)置欲诺,是否阻塞模式
    //注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長度,
    //所以在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完渺鹦。recv函數(shù)僅僅是copy數(shù)據(jù)扰法,真正的接收數(shù)據(jù)是協(xié)議來完成的
    recv(sockfd, buf, sizeof(buf), 10);
    printf("%s\n",buf);
    printf("please in put information:\n");
    while(1){
        bzero(buf,sizeof(buf));
/*      printf("%s\n","非阻塞 讀取");
        int flags;
        //使用非阻塞io
        if(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0)
        {
            perror("fcntl");
            return -1;
        }
        flags |= O_NONBLOCK;
        if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0)
        {
            perror("fcntl");
            return -1;
        }*/

        //鍵盤讀入.默認(rèn)為阻塞
        read(STDIN_FILENO, buf, sizeof(buf));
        //寫入到 根文件描述符
        if(send(sockfd, buf, sizeof(buf), 0) < 0){
            perror("send error!");
            exit(1);
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市毅厚,隨后出現(xiàn)的幾起案子塞颁,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祠锣,死亡現(xiàn)場離奇詭異酷窥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伴网,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門蓬推,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人澡腾,你說我怎么就攤上這事沸伏。” “怎么了动分?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵毅糟,是天一觀的道長。 經(jīng)常有香客問我澜公,道長留特,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任玛瘸,我火速辦了婚禮蜕青,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘糊渊。我一直安慰自己右核,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布渺绒。 她就那樣靜靜地躺著贺喝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宗兼。 梳的紋絲不亂的頭發(fā)上躏鱼,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機(jī)與錄音殷绍,去河邊找鬼染苛。 笑死,一個胖子當(dāng)著我的面吹牛主到,可吹牛的內(nèi)容都是我干的茶行。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼登钥,長吁一口氣:“原來是場噩夢啊……” “哼畔师!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起牧牢,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤看锉,失蹤者是張志新(化名)和其女友劉穎姿锭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伯铣,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艾凯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了懂傀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趾诗。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹬蚁,靈堂內(nèi)的尸體忽然破棺而出恃泪,到底是詐尸還是另有隱情,我是刑警寧澤犀斋,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布贝乎,位于F島的核電站,受9級特大地震影響叽粹,放射性物質(zhì)發(fā)生泄漏览效。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一虫几、第九天 我趴在偏房一處隱蔽的房頂上張望锤灿。 院中可真熱鬧,春花似錦辆脸、人聲如沸但校。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽状囱。三九已至,卻和暖如春倘是,著一層夾襖步出監(jiān)牢的瞬間亭枷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工搀崭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叨粘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓门坷,卻偏偏與公主長得像宣鄙,于是被迫代替她去往敵國和親袍镀。 傳聞我的和親對象是個殘疾皇子默蚌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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