FTP

1.項目概述

1.1項目概述

  • 本項目基于linux系統(tǒng)編程實現(xiàn),使用了linux文件操作與網(wǎng)絡編程等技術
  • 采用C/S模式設計,服務器與客戶端可相互發(fā)送任意大小文件,支持一些常用指令查看文件,如ls,cd,pwd

1.2大體實現(xiàn)方法

  • 由于需要傳輸大文件,因此需要將文件分包傳輸
  • 本項目采用UDP協(xié)議傳輸文件,由于UDP是面向無連接,不可靠傳輸,因此需要我們手動進行包管理
  • 在發(fā)送端將每個包編號,接收方每接收一個包就會發(fā)送確認信息給發(fā)送方,發(fā)送方在接收到確認消息后才會發(fā)送下一個包,否則就會重發(fā)
  • 每組包附帶crc8校驗,確保傳輸過程無差錯

1.3完整項目地址

2.項目實現(xiàn)細節(jié)

2.1crc8校驗算法

  • 關于crc8校驗算法,網(wǎng)上有很多大神的博客,此處參考

2.2通用結構體

  • 為保證收發(fā)雙方指令交互的方便,在此封裝了指令結構體
/*枚舉征椒,保存指令類型*/
typedef enum
{
    ls = 1,
    lls,
    pwd,
    lpwd,
    cd,
    lcd,
    get,
    put,
    help,
    quit
}te_cmd;

/*指令交互包*/
typedef struct cmd_msg
{
    te_cmd cmd;             /*指令類型*/
    char buff[BUF_SIZE];    /*指令附帶的額外內容,如put a.out的a.out*/
}ts_cmd_msg;
  • 還定義了一個數(shù)據(jù)包結構體,用于保存拆分的數(shù)據(jù)包
typedef struct package_msg
{
    int id;                 /*包ID*/
    int buff_size;          /*包大小*/
    unsigned char crc8;     /*包crc校驗碼*/
    int is_resend;          /*是否重發(fā)標志位*/
}ts_package_msg;
typedef struct package
{
    struct package_msg pmsg;
    char data_buf[PACK_SIZE];/*數(shù)據(jù)包具體傳輸內容*/
}ts_package;

2.3服務器細節(jié)實現(xiàn)

2.1.1主函數(shù)

int main(void)
{
    char cmd_nnum[1024] = {0};
    ts_cmd_msg cmd_pack;
    char clientIPV4[16];
    struct sockaddr_in sin;
    // socklen_t sin_len = sizeof(sin);
    struct sockaddr_in client_msg;
    socklen_t client_msg_len = sizeof(client_msg);
    int b_reuse = 1;
    int fd = -1;
    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("create socket failed in:socket():12");
        exit(-1);
    }
    /*允許綁定地址快速重用 */
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERV_PORT);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    /*綁定*/
    if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("bind failed in:bind():23");
        exit(-1);
    }
    printf("server init ok\n");
    while (1)
    {
        bzero(&client_msg, sizeof(client_msg));
        /*阻塞等待客戶端指令*/
        if(recvfrom(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&client_msg, &client_msg_len) < 0)
        {
            perror("recv msg failed in:recvfrom():30");
            break;
        }
        /*網(wǎng)絡字節(jié)序轉本地字節(jié)序*/
        if(!inet_ntop(AF_INET, (void *)&client_msg, clientIPV4, sizeof(client_msg)))
        {
            perror("can not know client mseeage in:inet_ntop()");
            exit(-1);
        }
        printf("Recive from(%s:%d) cmd is:%d\n", clientIPV4, ntohs(client_msg.sin_port), cmd_pack.cmd);
        /*解析指令*/
        if(enum_to_cmd(&cmd_pack, cmd_nnum, fd, &client_msg) < 0)
        {
            printf("parse command failed in:enum_to_cmd()\n");
        }
        /*不用回復客戶端的指令*/
        if(cmd_pack.cmd == get || cmd_pack.cmd == lls || cmd_pack.cmd == lpwd || cmd_pack.cmd == lcd || cmd_pack.cmd == put)
        {
            bzero(&cmd_pack, sizeof(cmd_pack));
        }
        else
        {
            resend_cmd_pack:
            //printf("send buff is:%s\n", cmd_pack.buff);/*DEBUG*/
            /*回復客戶端消息*/
            if((sendto(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&client_msg, client_msg_len)) < 0)
            {
                perror("send faied in:sendto()");
                goto resend_cmd_pack;
            }
            bzero(&cmd_pack, sizeof(cmd_pack));
        } 
    }
    return 0;
}

2.1.2解析指令函數(shù)

int enum_to_cmd(ts_cmd_msg *cmd_pack_local, char *s, int fd, struct sockaddr_in *cli_msg)
{   
    printf("cmd type is %d\n", cmd_pack_local->cmd);
    switch (cmd_pack_local->cmd)
    {
        case ls:
            strcpy(s, "ls");
            if(get_cmd_res(cmd_pack_local, s) < 0)
            {
                printf("get cmd res failed in:get_cmd_res()\n");
                return -1;
            }
            break;
        case pwd:
            strcpy(s, "pwd");
            if(get_cmd_res(cmd_pack_local, s) < 0)
            {
                printf("get cmd res failed in:get_cmd_res()\n");
                return -1;
            }
            break;
        case cd:
            printf("dir is %s\n", cmd_pack_local->buff);
            if(chdir(cmd_pack_local->buff) < 0)
            {
                perror("change dir failed in:chdir()");
                return -1;
            }
            if(get_cmd_res(cmd_pack_local, "pwd") < 0)
            {
                printf("get cmd res failed in:get_cmd_res()\n");
                return -1;
            }
            break;
        case lls:
        case lpwd:
        case lcd:
            break;
        case get:
            if(!access(cmd_pack_local->buff, F_OK))
            {
                sendto(fd, cmd_pack_local, sizeof(*cmd_pack_local), 0, (struct sockaddr *)cli_msg, sizeof(*cli_msg));
                printf("will send file\n");
                serv_send_file_process(fd, cli_msg, cmd_pack_local);
            }
            else
            {
                strcpy(cmd_pack_local->buff, "N");
                sendto(fd, cmd_pack_local, sizeof(*cmd_pack_local), 0, (struct sockaddr *)cli_msg, sizeof(*cli_msg));
                printf("No such file or directory\n");
            }
            //strcpy(cmd_pack_local->buff, "send done\n");
            break;
        case put:
            printf("will recv file\n");
            serv_recv_file_process(fd, cli_msg, cmd_pack_local);
            break;
        default:
            return -1;
    }
    return 0;
}

2.1.3獲取指令執(zhí)行結果

  • 函數(shù)popen
#include <stdio.h>

/*
功能:調用fork()產生子進程,然后在子進程中執(zhí)行參數(shù)command的指令
參數(shù):
參數(shù)1:指令
參數(shù)2:'r'代表讀取,'w'代表寫入,如果'r',那么調用進程讀進command的標準輸出,
        如果為'w',那么調用進程寫到command的標準輸入
返回值:成功則返回文件指針,否則返回NULL
*/
FILE *popen(const char *command, const char *type);
  • 代碼
int get_cmd_res(ts_cmd_msg *cmd_pack_local, char *s)
{
    int rsize = 0;
    printf("cmd is %s\n", s);/*DEBUG*/
    FILE *dir = popen(s, "r");
    if(dir == NULL)
    {
        perror("fail to open dir in:popen()");
        return -1;
    }
    bzero(cmd_pack_local->buff, BUF_SIZE);
    if((rsize = fread(cmd_pack_local->buff, sizeof(char), BUF_SIZE, dir)) < 0)
    {
        perror("get cmd res failed in:fread()");
        return -1;
    }
    //printf("popen buff is:%s\n", cmd_pack_local->buff);/*DEBUG*/
    pclose(dir);
    return rsize;
}

2.1.4服務器發(fā)數(shù)據(jù)

int serv_send_file_process(int fd, struct sockaddr_in *cli_msg, ts_cmd_msg *cmd_pack_local)
{
    socklen_t client_msg_len = sizeof(*cli_msg);
    char cli_ip[16] = {0};
    if(!inet_ntop(AF_INET, (void *)cli_msg, cli_ip, sizeof(*cli_msg)))
    {
        perror("can not know client mseeage in:inet_ntop()");
        exit(-1);
    }
    printf("start send to %s:%d\n", cli_ip, ntohs(cli_msg->sin_port));
    FILE *file;
    //file = fopen("test.gif", "rb");
    file = fopen(cmd_pack_local->buff, "rb");
    if(file == NULL)
    {
        perror("file open failed");
        return -1;
    }
    printf("open file success\n");
    int pack_id = 1;
    int check_id = 0;
    int pack_size = 0;
    int send_size = 0;
    ts_package pack;
    ts_package_msg check_msg;
    bzero(&pack, sizeof(pack));
    /*每次讀取PACK_SIZE個字節(jié)*/
    while ((pack_size = fread(pack.data_buf, sizeof(char), PACK_SIZE, file)) > 0)
    {
        pack.pmsg.id = pack_id;
        pack.pmsg.buff_size = pack_size;
        pack.pmsg.crc8 = cal_crc_table(pack.data_buf, pack_size);
        pack.pmsg.is_resend = 0;
        
        /*重發(fā)標簽*/
        resend:
        if((send_size = sendto(fd, &pack, sizeof(pack), 0, (struct sockaddr *)cli_msg, client_msg_len)) < 0)
        {
            perror("send faied in:sendto()");
            goto resend;
        }
        printf("sending pack %d, size %d Byte\n", pack.pmsg.id, send_size);
        /*接收客戶端反饋信息*/
        recvfrom(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)cli_msg, &client_msg_len);
        /*包錯誤慰照。重發(fā)*/
        if(check_msg.is_resend == 1)
        {
            check_id = check_msg.id;
            printf("pack %d err\n", check_id);
            goto resend;
        }
        else
        {
            pack_id++;
            bzero(&pack, sizeof(pack));
        }
    }  
    /*發(fā)送結束標志,即空的結構體*/
    sendto(fd, &pack, 0, 0, (struct sockaddr *)cli_msg, client_msg_len);
    printf("send done...\n");
    fclose(file);
    return 0;
}

2.1.5服務器接收數(shù)據(jù)

int serv_recv_file_process(int fd, struct sockaddr_in *cli_msg, ts_cmd_msg *cmd_pack_local)
{
    FILE *file;
    file = fopen(cmd_pack_local->buff, "wb");
    if(file == NULL)
    {
        perror("open file failed in:fopen()");
    }
    printf("open file success\n");
    int recv_len = 0;
    int check_pack_id = 1;
    ts_package pack;
    unsigned char check_crc;
    ts_package_msg check_msg;
    bzero(&pack, sizeof(pack));
    socklen_t cli_msg_len = sizeof(*cli_msg);
    /*接收數(shù)據(jù)包*/
    while((recv_len = recvfrom(fd, &pack, sizeof(pack), 0, (struct sockaddr *)cli_msg, &cli_msg_len)) > 0)
    {
        //printf("in while\n");
        printf("recving %d pack, size %d Byte\n", pack.pmsg.id, recv_len);
        check_crc = cal_crc_table(pack.data_buf, pack.pmsg.buff_size);
        /*檢測數(shù)據(jù)包順序是否發(fā)送正常*/
        if(check_pack_id == pack.pmsg.id)
        {
            /*crc8校驗*/
            if(pack.pmsg.crc8 == check_crc)
            {
                /*成功接收包,發(fā)送反饋信息*/
                check_msg.id = check_pack_id++;
                check_msg.is_resend = 0;
                fwrite(pack.data_buf, sizeof(char), pack.pmsg.buff_size, file);
                bzero(&pack, sizeof(pack));
                sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)cli_msg, cli_msg_len);
            }
            else
            {
                printf("crc8 check err\n");
                check_msg.id = check_pack_id;
                /*將錯誤標志置為1*/
                check_msg.is_resend = 1;
                /*清空錯誤數(shù)據(jù)包*/
                bzero(&pack, sizeof(pack));
                /*發(fā)送錯誤信息*/
                sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)cli_msg, cli_msg_len);
            }
        }
        else
        {
            printf("pack id err\n");
            check_msg.id = check_pack_id;
            check_msg.is_resend = 1;
            bzero(&pack, sizeof(pack));
            sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)cli_msg, cli_msg_len);
        }
    }
    printf("recv done\n");
    fclose(file);
    //bzero(cmd_pack_local, sizeof(*cmd_pack_local));
    return 0;
}

2.4客戶端細節(jié)實現(xiàn)

2.4.1主函數(shù)

int main(int argc, char ** argv)
{
    int port = -1;
    int fd = -1;
    ts_cmd_msg cmd_pack;
    struct sockaddr_in cin;
    socklen_t cin_len = sizeof(cin);
    // char buff[64] = {0};
    // int ret = -1;
    if(argc != 3)
    {
        tips(argv[0]);
        exit(-1);
    }
    port = atoi(argv[2]);
    if(port < 5000)
    {
        tips(argv[0]);
        exit(-1); 
    }
    /*創(chuàng)建socket */
    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket");
        exit(-1);
    }
    /*填充結構體康震,即目標服務器IP和端口 */
    bzero(&cin, sizeof(cin));
    cin.sin_family = AF_INET;
    cin.sin_port = htons(port);
    if(inet_pton(AF_INET, argv[1], (void *)&cin.sin_addr.s_addr) != 1)
    {
        perror("inet_pton");
        exit(-1);
    }
    puts("UDP client start ok");
    bzero(&cmd_pack, sizeof(cmd_pack));
    while (1)
    {
        char input_cmd[50] = {0};
        int re_cmd = 0;
        printf(">");
        if((fgets(input_cmd, 50, stdin)) == NULL)
        {
            perror("fgets");
        }
        char *s = strstr(input_cmd, "\n");
        strcpy(s, "\0");/*去換行符*/
        if((re_cmd = cmd_to_enum(&cmd_pack, input_cmd)) < 0)
        {
            printf("err cmd\n");
        }
        else if(re_cmd > 0)
        {
            break;
        }
        switch (cmd_pack.cmd)
        {
            case get:
                sendto(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&cin, cin_len);
                recvfrom(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&cin, &cin_len);
                if(!strcmp(cmd_pack.buff, "N"))
                {
                    printf("No such file or directory\n");
                }
                else
                {
                    cli_recv_file_process(fd, &cin, &cmd_pack);
                }
                break;
            case put:
                if(!access(cmd_pack.buff, F_OK))
                {
                    sendto(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&cin, cin_len);
                    cli_send_file_process(fd, &cin, &cmd_pack);
                }
                else
                {
                    printf("No such file or directory\n");
                }
                break;
            case lls:
            case lpwd:
            case lcd:
                break;
            default:
                /*發(fā)送指令*/
                sendto(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&cin, cin_len);
                bzero(&cmd_pack, sizeof(cmd_pack));
                /*接收消息*/
                recvfrom(fd, &cmd_pack, sizeof(cmd_pack), 0, (struct sockaddr *)&cin, &cin_len);
                printf("%s", cmd_pack.buff);
                //printf("%s\n", cmd_pack.buff);
                break;
        } 
    }
    return 0;
}

2.4.2指令解析函數(shù)

int cmd_to_enum(ts_cmd_msg *cmd_pack_local, char *s)
{
    char cmd[50] = {0};
    strncpy(cmd, s, 50);
    //printf("cmd is %s\n", cmd);/*DEBUG*/
    if(!strcmp("ls", cmd))
    {
        cmd_pack_local->cmd = ls;
        return 0;
    }
    if(!strcmp("lls", cmd))
    {
        system("ls");
        cmd_pack_local->cmd = lls;
        return 0;
    }                
    if(!strcmp("pwd", cmd))
    {
        cmd_pack_local->cmd = pwd;
        return 0;
    }     
    if(!strcmp("lpwd", cmd))
    {
        system("pwd");
        cmd_pack_local->cmd = lpwd;
        return 0;
    }             
    if(!strncmp(cmd, "cd ", strlen("cd ")))
    {
        char *s = strstr(cmd, "cd ");
        // printf("input:%s", s+strlen("cd "));
        cmd_pack_local->cmd = cd;
        strcpy(cmd_pack_local->buff, s+strlen("cd "));
        return 0;
    }
    if(!strncmp(cmd, "lcd ", strlen("lcd ")))
    {
        char *s = strstr(cmd, "lcd ");
        // printf("input:%s", s+strlen("cd "));
        cmd_pack_local->cmd = lcd;
        chdir(s+strlen("lcd "));
        system("pwd");
        return 0;
    }
    if(!strncmp(cmd, "get ", strlen("get ")))
    {
        char *s = strstr(cmd, "get ");
        // printf("input:%s", s+strlen("cd "));
        cmd_pack_local->cmd = get;
        strcpy(cmd_pack_local->buff, s+strlen("get "));
        // recv_file_process(fd, serv_msg, cmd_pack_local);
        return 0;
    }
    if(!strncmp(cmd, "put ", strlen("put ")))
    {
        char *s1 = strstr(cmd, "put ");
        cmd_pack_local->cmd = put;
        strcpy(cmd_pack_local->buff, s1+strlen("put "));
        return 0;
    }
    if(!strcmp("quit", cmd))
    {
        return 1;
    } 
    return -1;
}

2.4.3客戶端接收數(shù)據(jù)

int cli_recv_file_process(int fd, struct sockaddr_in *serv_msg, ts_cmd_msg *cmd_pack_local)
{
    FILE *file;
    file = fopen(cmd_pack_local->buff, "wb");
    if(file == NULL)
    {
        perror("open file failed in:fopen()");
    }
    printf("open file success\n");
    int recv_len = 0;
    int check_pack_id = 1;
    ts_package pack;
    unsigned char check_crc;
    ts_package_msg check_msg;
    bzero(&pack, sizeof(pack));
    socklen_t serv_msg_len = sizeof(*serv_msg);
    while((recv_len = recvfrom(fd, &pack, sizeof(pack), 0, (struct sockaddr *)serv_msg, &serv_msg_len)) > 0)
    {
        //printf("in while\n");
        printf("recving %d pack, size %d Byte\n", pack.pmsg.id, recv_len);
        check_crc = cal_crc_table(pack.data_buf, pack.pmsg.buff_size);
        if(check_pack_id == pack.pmsg.id)
        {
            if(pack.pmsg.crc8 == check_crc)
            {
                /*成功接收包,發(fā)送反饋信息*/
                check_msg.id = check_pack_id++;
                check_msg.is_resend = 0;
                fwrite(pack.data_buf, sizeof(char), pack.pmsg.buff_size, file);/*(待補全)錯誤處理*/
                bzero(&pack, sizeof(pack));
                sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)serv_msg, serv_msg_len);
            }
            else
            {
                printf("crc8 check err\n");
                check_msg.id = check_pack_id;
                check_msg.is_resend = 1;
                bzero(&pack, sizeof(pack));
                sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)serv_msg, serv_msg_len);
            }
        }
        else
        {
            printf("pack id err\n");
            check_msg.id = check_pack_id;
            check_msg.is_resend = 1;
            bzero(&pack, sizeof(pack));
            sendto(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)serv_msg, serv_msg_len);
        }
    }
    fclose(file);
    printf("recv done\n");
    //bzero(cmd_pack_local, sizeof(*cmd_pack_local));
    return 0;
}

2.4.4客戶端發(fā)送數(shù)據(jù)

int cli_send_file_process(int fd, struct sockaddr_in *serv_msg, ts_cmd_msg *cmd_pack_local)
{
    socklen_t serv_msg_len = sizeof(*serv_msg);
    printf("start send to server\n");
    FILE *file;
    //file = fopen("test.gif", "rb");
    file = fopen(cmd_pack_local->buff, "rb");
    if(file == NULL)
    {
        perror("file open failed");
        return -1;
    }
    printf("open file success\n");
    int pack_id = 1;
    int check_id = 0;
    int pack_size = 0;
    int send_size = 0;
    ts_package pack;
    ts_package_msg check_msg;
    bzero(&pack, sizeof(pack));
    while ((pack_size = fread(pack.data_buf, sizeof(char), PACK_SIZE, file)) > 0)
    {
        pack.pmsg.id = pack_id;
        pack.pmsg.buff_size = pack_size;
        pack.pmsg.crc8 = cal_crc_table(pack.data_buf, pack_size);
        pack.pmsg.is_resend = 0;
        
        resend:
        if((send_size = sendto(fd, &pack, sizeof(pack), 0, (struct sockaddr *)serv_msg, serv_msg_len)) < 0)
        {
            perror("send faied in:sendto()");
            goto resend;
        }
        printf("sending pack %d, size %d Byte\n", pack.pmsg.id, send_size);
        recvfrom(fd, &check_msg, sizeof(check_msg), 0, (struct sockaddr *)serv_msg, &serv_msg_len);
        if(check_msg.is_resend == 1)
        {
            check_id = check_msg.id;
            printf("pack %d err\n", check_id);
            goto resend;
        }
        else
        {
            pack_id++;
            bzero(&pack, sizeof(pack));
        }
    }  
    sendto(fd, &pack, 0, 0, (struct sockaddr *)serv_msg, serv_msg_len);
    bzero(cmd_pack_local, sizeof(*cmd_pack_local));
    printf("send done...\n");
    fclose(file);
    return 0;
}

2.5Makefile

TAR1 = server
TAR2 = client
TAR3 = crc8
CC := gcc
SRC = $(wildcard ./Src/*.c)
OBJS = $(patsubst %.c, %.o, $(wildcard ./Src/*.c))
CFLAGS = -c -Wall -I Inc

all:$(TAR1) $(TAR2)
$(TAR1):$(OBJS)
    $(CC) ./Src/$(TAR1).o ./Src/$(TAR3).o -o $(TAR1)
$(TAR2):$(OBJS)
    $(CC) ./Src/$(TAR2).o ./Src/$(TAR3).o -o $(TAR2)
%.o:%.c
    $(CC) $(CFLAGS) $^ -o $@

.PHONY:
clean_all:
    rm $(OBJS) $(TAR1) $(TAR2)
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卷中,隨后出現(xiàn)的幾起案子政恍,更是在濱河造成了極大的恐慌氓涣,老刑警劉巖牛哺,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異春哨,居然都是意外死亡荆隘,警方通過查閱死者的電腦和手機恩伺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門赴背,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晶渠,你說我怎么就攤上這事凰荚。” “怎么了褒脯?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵便瑟,是天一觀的道長。 經(jīng)常有香客問我番川,道長到涂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任颁督,我火速辦了婚禮践啄,結果婚禮上,老公的妹妹穿的比我還像新娘沉御。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布苛聘。 她就那樣靜靜地躺著够掠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪试疙。 梳的紋絲不亂的頭發(fā)上诵棵,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音祝旷,去河邊找鬼履澳。 笑死柱徙,一個胖子當著我的面吹牛,可吹牛的內容都是我干的奇昙。 我是一名探鬼主播护侮,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼储耐!你這毒婦竟也來了羊初?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤什湘,失蹤者是張志新(化名)和其女友劉穎长赞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闽撤,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡得哆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哟旗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贩据。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闸餐,靈堂內的尸體忽然破棺而出饱亮,到底是詐尸還是另有隱情,我是刑警寧澤舍沙,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布近上,位于F島的核電站,受9級特大地震影響拂铡,放射性物質發(fā)生泄漏壹无。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一感帅、第九天 我趴在偏房一處隱蔽的房頂上張望斗锭。 院中可真熱鬧,春花似錦留瞳、人聲如沸拒迅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽璧微。三九已至,卻和暖如春硬梁,著一層夾襖步出監(jiān)牢的瞬間前硫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工荧止, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屹电,地道東北人阶剑。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像危号,于是被迫代替她去往敵國和親牧愁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355