ESP32學習筆記(9)——TCP服務端

一陵珍、TCP與UDP優(yōu)缺點

1寝杖、TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接互纯。

2瑟幕、TCP提供可靠的服務。也就是說留潦,通過TCP連接傳送的數(shù)據(jù)只盹,無差錯,不丟失兔院,不重復殖卑,且按序到達;UDP盡最大努力交付,即不保證可靠交付坊萝。
TCP通過校驗和孵稽,重傳控制,序號標識十偶,滑動窗口菩鲜、確認應答實現(xiàn)可靠傳輸。如丟包時的重發(fā)控制扯键,還可以對次序亂掉的分包進行順序控制睦袖。

3、UDP具有較好的實時性荣刑,工作效率比TCP高馅笙,適用于對高速傳輸和實時性有較高的通信或廣播通信。

4厉亏、每一條TCP連接只能是點到點的;UDP支持一對一董习,一對多,多對一和多對多的交互通信爱只。

5皿淋、TCP對系統(tǒng)資源要求較多,UDP對系統(tǒng)資源要求較少恬试。

二窝趣、概述

ESP-IDF使用開源 lwIP輕量級的TCP / IP堆棧。ESP-IDF版本lwIP(esp-lwip)與上游項目相比有一些修改和補充训柴。

ESP-IDF支持以下功能 lwIP TCP / IP堆棧功能:

BSD套接字API

BSD套接字API是一個通用的跨平臺TCP / IP套接字API,該API起源于UNIX的Berkeley標準發(fā)行版幻馁,但現(xiàn)在已在POSIX規(guī)范的一部分中進行了標準化洗鸵。BSD套接字有時稱為POSIX套接字或Berkeley套接字越锈。

正如ESP-IDF中實施的那樣,lwIP支持BSD套接字API的所有常用用法膘滨。

ESP-IDF 編程指南——ESP-NETIF
ESP-IDF 編程指南——lwIP

三甘凭、API說明

以下 BSD Socket 接口位于 lwip/lwip/src/include/lwip/sockets.h

  • socket()
  • bind()
  • accept()
  • shutdown()
  • getpeername()
  • getsockopt()setsockopt()(請參閱套接字選項
  • close()(通過虛擬文件系統(tǒng)組件
  • read()火邓,readv()丹弱,write()writev()(經(jīng)由虛擬文件系統(tǒng)部件
  • recv()贡翘,recvmsg()蹈矮,recvfrom()
  • send()sendmsg()鸣驱,sendto()
  • select()(通過虛擬文件系統(tǒng)組件
  • poll()(注意:在ESP-IDF上泛鸟,poll()是通過內(nèi)部調(diào)用select來實現(xiàn)的,因此踊东,select()如果有可用的方法選擇北滥,建議直接使用。)
  • fcntl()(請參閱fcntl

非標準功能:

  • ioctl()(請參閱ioctls

四闸翅、TCP服務端

4.1 主要流程

4.1.1 第一步:新建socket

int addr_family = 0;
int ip_protocol = 0;

addr_family = AF_INET;
ip_protocol = IPPROTO_IP;

int listen_sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
if(listen_sock < 0) 
{
    ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
}

4.1.2 第二步:配置服務器信息

#define TCP_PORT             3333                // TCP服務器端口號

struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_port = htons(TCP_PORT);

4.1.3 第三步:綁定地址

int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if(err != 0) 
{
    ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
    ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
    close(listen_sock);
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);

4.1.4 第四步:開始監(jiān)聽

err = listen(listen_sock, 1);    // 這里為啥是1再芋,網(wǎng)上大多數(shù)是5
if(err != 0) 
{
    ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
    close(listen_sock);
}

4.1.5 第五步:等待客戶端連接

while(1)
{
    struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
    uint addr_len = sizeof(source_addr);
    int connect_sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
    if(connect_sock < 0) 
    {
        ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
        close(listen_sock);
    }    
}

4.1.6 第六步:接收數(shù)據(jù)

int len;
char rx_buffer[128];

while(1)
{
    memset(rx_buffer, 0, sizeof(rx_buffer));    // 清空緩存        
    len = recv(connect_sock, rx_buffer, sizeof(rx_buffer), 0);  // 讀取接收數(shù)據(jù)
    if(len < 0) 
    {
        ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
    } 
    else if (len == 0) 
    {
        ESP_LOGW(TAG, "Connection closed");
    } 
    else 
    {
        ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
    }
}

4.1.7 第七步:發(fā)送數(shù)據(jù)

send(connect_socket, rx_buffer, sizeof(rx_buffer, 0);

4.2 配置SSID和密碼連接WIFI創(chuàng)建TCP服務端

TCP Server類似


使用 esp-idf\examples\protocols\sockets\tcp_server 中的例程

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


#define PORT CONFIG_EXAMPLE_PORT

static const char *TAG = "example";

static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[128];

    do {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        } else if (len == 0) {
            ESP_LOGW(TAG, "Connection closed");
        } else {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // send() can return less bytes than supplied length.
            // Walk-around for robust implementation. 
            int to_write = len;
            while (to_write > 0) {
                int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
                if (written < 0) {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                }
                to_write -= written;
            }
        }
    } while (len > 0);
}

static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    } else if (addr_family == AF_INET6) {
        bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Convert ip address to string
        if (source_addr.sin6_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
        } else if (source_addr.sin6_family == PF_INET6) {
            inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect());

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
#endif
}

idf.py menuconfig 配置服務器端口


配置SSID和密碼



然后 idf.py flash 編譯下載

查看打印:
連接上WIFI后查看獲取到的IP地址(如192.168.61.107)


打開TCP客戶端坚冀,輸入服務端IP和端口济赎,選擇連接,發(fā)送數(shù)據(jù)

4.3 作為AP創(chuàng)建TCP服務端

根據(jù) esp-idf\examples\protocols\sockets\tcp_server 中的例程修改

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


#define PORT CONFIG_EXAMPLE_PORT

#define EXAMPLE_ESP_WIFI_SSID      "ESP32_TEST"
#define EXAMPLE_ESP_WIFI_PASS      "12345678"
#define EXAMPLE_ESP_WIFI_CHANNEL   1
#define EXAMPLE_MAX_STA_CONN       4

static const char *TAG = "example";

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

void wifi_init_softap(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_ap();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}

static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[128];

    do {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        } else if (len == 0) {
            ESP_LOGW(TAG, "Connection closed");
        } else {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // send() can return less bytes than supplied length.
            // Walk-around for robust implementation. 
            int to_write = len;
            while (to_write > 0) {
                int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
                if (written < 0) {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                }
                to_write -= written;
            }
        }
    } while (len > 0);
}

static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    } else if (addr_family == AF_INET6) {
        bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Convert ip address to string
        if (source_addr.sin6_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
        } else if (source_addr.sin6_family == PF_INET6) {
            inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    // ESP_ERROR_CHECK(esp_netif_init());
    // ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    // ESP_ERROR_CHECK(example_connect());
    wifi_init_softap();

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
#endif
}

idf.py menuconfig 配置服務器端口


然后 idf.py flash 編譯下載

查看打蛹悄场:


IP默認是192.168.4.1 這個是樂鑫在出廠時候?qū)懰赖?p>

打開TCP客戶端司训,輸入服務端IP和端口,選擇連接液南,發(fā)送數(shù)據(jù)



? 由 Leung 寫于 2021 年 4 月 28 日

? 參考:樂鑫Esp32學習之旅⑨ esp32上實現(xiàn)本地 TCP 客戶端和服務端角色壳猜,可斷線重連原路返回數(shù)據(jù)
    ESP32 開發(fā)筆記(三)源碼示例 20_WIFI_STA_TCP_Server 在站模式STA下實現(xiàn)TCP服務端
    ESP32 開發(fā)筆記(三)源碼示例 16_WIFI_AP_TCP_Server 在AP模式下實現(xiàn)TCP服務端

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市滑凉,隨后出現(xiàn)的幾起案子统扳,更是在濱河造成了極大的恐慌,老刑警劉巖畅姊,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咒钟,死亡現(xiàn)場離奇詭異,居然都是意外死亡若未,警方通過查閱死者的電腦和手機朱嘴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陨瘩,“玉大人腕够,你說我怎么就攤上這事∩嗬停” “怎么了帚湘?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甚淡。 經(jīng)常有香客問我大诸,道長,這世上最難降的妖魔是什么贯卦? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任资柔,我火速辦了婚禮,結(jié)果婚禮上撵割,老公的妹妹穿的比我還像新娘贿堰。我一直安慰自己,他們只是感情好啡彬,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布羹与。 她就那樣靜靜地躺著,像睡著了一般庶灿。 火紅的嫁衣襯著肌膚如雪纵搁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天往踢,我揣著相機與錄音腾誉,去河邊找鬼。 笑死峻呕,一個胖子當著我的面吹牛利职,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播山上,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼眼耀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佩憾?” 一聲冷哼從身側(cè)響起哮伟,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妄帘,沒想到半個月后楞黄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡抡驼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年鬼廓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片致盟。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡碎税,死狀恐怖尤慰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雷蹂,我是刑警寧澤伟端,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站匪煌,受9級特大地震影響责蝠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜萎庭,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一霜医、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驳规,春花似錦肴敛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至巩搏,卻和暖如春昨登,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贯底。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工丰辣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人禽捆。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓笙什,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胚想。 傳聞我的和親對象是個殘疾皇子琐凭,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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