go socket close那些事

溫故而知新吞杭,可以為師矣

基于目前socket在編程中的重要地位争剿,我打算仔細(xì)琢磨一下go的socket編程羽利。打造一個屬于自己的方便調(diào)用的go socket框架捻勉。因為畢竟自己有c的socket經(jīng)驗,所以我覺得應(yīng)該沒什么難度虹茶。

一路下來也確實沒有碰到什么問題逝薪,在此就大致記錄下編碼過程以及新體會吧。


編寫go客戶端:
  1. 獲取服務(wù)器ip地址
    ip, err := net.ResolveIPAddr("", "127.0.0.1")
    if err != nil {
      Log("err1=%v\n", err.Error())  
      return
    }
  1. 建立連接
    ipStr := fmt.Sprintf("%s:20003", ip.IP.String())
    conn, err := net.Dial("tcp", ipStr)
    if err != nil {
        Log("err2=%v\n", err.Error())
        return
    }
  1. 數(shù)據(jù)處理
    defer func() {
        err := conn.Close()
        if err != nil {
            Log("err3=%v\n", err.Error())
            return
        }
    }()

    Log("connected ...\n")
    hello := "Hi,here is glp"
    n, err := conn.Write([]byte(hello))
    if err != nil {
        Log("err4=%v\n", err.Error())
        return
    }
    Log("send server [%d]=%s\n", n, hello)

    <-time.After(time.Second)
編寫go服務(wù)端:
  1. 首先獲取到一個監(jiān)聽的IP地址:
//獲取服務(wù)器監(jiān)聽ip地址
    ip, err := net.ResolveTCPAddr("", ":20003")
    if err != nil {
        Log("err1=%v\n", err.Error())
        return
    }
  1. 創(chuàng)建監(jiān)聽的socket
//創(chuàng)建一個監(jiān)聽的socket
conn, err := net.ListenTCP("tcp", ip)
if err != nil {
    Log("err2=%v\n", err.Error())
    return
}
  1. 服務(wù)器邏輯主循環(huán)
clientConn, err := conn.Accept()
if err != nil {
    Log("err3=%v\n", err.Error())
    return
}
Log("new client conn=%v,ip=%v\n", clientConn, clientConn.RemoteAddr())
go handlerClient(&StatusConn{clientConn, StatusNormal})
  1. 對客戶端socket的處理邏輯主要片段
buffer := make([]byte, 1024)
err := conn.SetReadDeadline(time.Now().Add(time.Second * 10))
if err != nil {
    Log("handlerClient err2=%v\n", err.Error())
    conn.Status = StatusError
    return
}
n, err := conn.Read(buffer)
if err != nil {
    Log("handlerClient err3=%v\n", err.Error())
    if err == io.EOF {
        conn.Status = StatusClosed
    } else {
        conn.Status = StatusError
    }
    return
}

clientBytes := buffer[:n]
Log("read client [%d]=%s\n", n, string(clientBytes))
hello := fmt.Sprintf("from server back【go】:%s", string(clientBytes))

n, err = conn.Write([]byte(hello))
Log("send client [%d]=%s\n", n, hello)
if err != nil {
    Log("handlerClient err4=%v\n", err.Error())
}
實驗

直接啟動服務(wù)器跟客戶端蝴罪,發(fā)現(xiàn)打印如下:
客戶端:

2019/05/30 16:35:44.255:connected ...
2019/05/30 16:35:44.269:send server [14]=Hi,here is glp

服務(wù)端:

2019/05/30 16:35:39.543:cur goos=windows
2019/05/30 16:35:39.560:server wait...
2019/05/30 16:35:44.255:new client conn=&{{0xc0000862c0}},ip=127.0.0.1:56795
2019/05/30 16:35:44.269:read client [14]=Hi,here is glp
2019/05/30 16:35:44.269:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 16:35:45.269:handlerClient err3=read tcp 127.0.0.1:20003->127.0.0.1:56795: wsarecv: An existing connection was forcibly closed by the remote host.
2019/05/30 16:35:45.269:handlerClient Status=3

是這個報錯內(nèi)容:An existing connection was forcibly closed by the remote host.
意思應(yīng)該是連接被遠(yuǎn)端強(qiáng)制關(guān)閉董济,誒。要门。這個跟io.EOF到底有什么區(qū)別呢虏肾。我記得我在編寫c++ socket的時候好像沒碰到這個。印象中強(qiáng)制關(guān)閉的判斷應(yīng)該就是判斷recv(...)==0欢搜。帶著這個好奇封豪,我于是編寫了c++ socket服務(wù)器。

c++服務(wù)器 主要邏輯代碼:
  1. 服務(wù)器主循環(huán)
int main() {
    Env env = Env();

    SOCKET listenFd = -1;
    listenFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//socket 描述符
    if (INVALID_SOCKET == listenFd)
    {
        Printf("create socket error %d\n", errno);
        return -1;
    }
    Printf("服務(wù)器創(chuàng)建網(wǎng)絡(luò)描述符 fd=%d\n", listenFd);

    if (setAddrReuse(listenFd) != 0) {
        Printf("setAddrReuse error %d\n", errno);
        return -1;
    }
    Printf("服務(wù)器設(shè)置地址重用成功\n");

    struct sockaddr_in ipAddr = { 0 };
    ipAddr.sin_family = AF_INET;
    ipAddr.sin_port = htons(20003);
    ipAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (0 != bind(listenFd, (struct sockaddr*) & ipAddr, sizeof(ipAddr)))
    {
        Printf("socket bind error %d\n", errno);
        return -1;
    }
    Printf("服務(wù)器綁定地址成功\n");

    if (::listen(listenFd, SOMAXCONN) != 0) {
        Printf("socket listen error %d\n", errno);
        return -1;
    }
    Printf("服務(wù)器監(jiān)聽成功\n");

    sockaddr addr;
#ifdef WIN32
    int len = sizeof(addr);
#else
    socklen_t len = sizeof(addr);
#endif
    while (true) {
        SOCKET clientSocket = accept(listenFd, &addr, &len);
        if (clientSocket == INVALID_SOCKET) {
            Printf("socket accept error %d\n", errno);
            return -1;
        }

        Printf("客戶端新連接:接入 fd = %d,ip = %s\n", clientSocket, GetPeerIp(clientSocket));
        auto clientThread = std::thread(handlerClient, clientSocket);
        clientThread.detach();
    }
}
  1. 客戶端連接處理片段
        char buf[65535] = { 0 };
        int len = (int) ::recv(fd, buf, sizeof(buf), 0);//接收網(wǎng)絡(luò)數(shù)據(jù)
        if (len == 0) {
            Printf("客戶端連接被主動關(guān)閉\n");
            return;
        }

        if (len == SOCKET_ERROR) {
            int errorcode;
#ifdef WIN32
            errorcode = GetLastError();//這里的錯誤碼炒瘟,必須用windows的GetLastError獲取吹埠。
            if (errorcode != WSAEWOULDBLOCK) {
#else//Linux
            errorcode = errno;
            if (errorcode != EAGAIN) {
#endif
                Printf("client[%d] recv err=%d\n", fd, errorcode);
                return;
            }
            continue;
        }

        Printf("client[%d] recv[%d]=%s\n", fd, len, buf);
        char hello[65535] = { 0 };
        sprintf(hello, "from server back[c++]:%s", buf);
        Printf("send to client[%d]=%s\n", fd, hello);
        ::send(fd, hello, strlen(hello), 0);
實驗2,用go客戶端連接這個c++服務(wù)器看打印結(jié)果

客戶端打印

2019/05/30 16:30:30.380:connected ...
2019/05/30 16:30:30.393:send server [14]=Hi,here is glp

c++服務(wù)器打印

2019-05-30 16:30:20.499:服務(wù)器創(chuàng)建網(wǎng)絡(luò)描述符 fd=636
2019-05-30 16:30:20.499:服務(wù)器設(shè)置地址重用成功
2019-05-30 16:30:20.499:服務(wù)器綁定地址成功
2019-05-30 16:30:20.499:服務(wù)器監(jiān)聽成功
2019-05-30 16:30:30.381:客戶端新連接:接入 fd = 640,ip = 127.0.0.1
2019-05-30 16:30:30.393:client[640] recv[14]=Hi,here is glp
2019-05-30 16:30:30.393:send to client[640]=from server back[c++]:Hi,here is glp
2019-05-30 16:30:31.393:client[640] recv err=10054

特地去查了下10054:

//
// MessageId: WSAECONNRESET
//
// MessageText:
//
// An existing connection was forcibly closed by the remote host.
//
#define WSAECONNRESET                    10054L

發(fā)現(xiàn)跟go服務(wù)器打印的錯誤信息一致疮装。缘琅。。
c++的recv函數(shù)在這里也確實返回了-1廓推,看來并不是所有的客戶端socket close刷袍,都可以讓服務(wù)器的recv得到0》梗看來是我自己以前沒怎么關(guān)注這塊只顧寫邏輯去了呻纹。。go的底層socket代碼應(yīng)該是跟c++保持一致的专缠。

實驗3 linux服務(wù)器

上面的服務(wù)器我都是在本地windows運行的雷酪,現(xiàn)在嘗試在linux上看看。
我把編寫的go服務(wù)器代碼編譯發(fā)布到服務(wù)器上再看服務(wù)器打印日志:

2019/05/30 16:45:00.012:cur goos=linux
2019/05/30 16:45:00.012:server wait...
2019/05/30 16:46:36.104:new client conn=&{{0xc00009a080}},ip=115.192.79.216:56919
2019/05/30 16:46:36.132:read client [14]=Hi,here is glp
2019/05/30 16:46:36.132:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 16:46:37.133:handlerClient err3=read tcp 172.16.200.43:20003->115.192.79.216:56919: read: connection reset by peer
2019/05/30 16:46:37.133:handlerClient Status=3

到了linux這邊就變成了connection reset by peer.
可以看到windows和linux返回的錯誤提示是不同的藤肢。不過他們想表達(dá)的意思應(yīng)該是一致的太闺。

實驗4 嘗試讀取服務(wù)器發(fā)來的數(shù)據(jù)再關(guān)閉socket

上面的go客戶端是沒有讀取服務(wù)器返回的數(shù)據(jù),自己等待一秒鐘就關(guān)閉了∴胰Γ現(xiàn)在把客戶端邏輯改成:

    Log("connected ...\n")
    hello := "Hi,here is glp"
    n, err := conn.Write([]byte(hello))
    if err != nil {
        Log("err4=%v\n", err.Error())
        return
    }
    Log("send server [%d]=%s\n", n, hello)
//新增讀取服務(wù)器數(shù)據(jù)代碼 begin
    buffer := make([]byte, 1024)
    n, err = conn.Read(buffer)
    if err != nil {
        Log("err5=%v\n", err.Error())
        return
    }
    Log("read server [%d]=%s\n", n, string(buffer))
//新增讀取服務(wù)器數(shù)據(jù)代碼 end

    <-time.After(time.Second)

再去請求go服務(wù)器和c++服務(wù)器
go服務(wù)器打印(linux):

2019/05/30 17:02:28.809:cur goos=linux
2019/05/30 17:02:28.809:server wait...
2019/05/30 17:02:43.239:new client conn=&{{0xc00009a080}},ip=115.192.79.216:57079
2019/05/30 17:02:43.253:read client [14]=Hi,here is glp
2019/05/30 17:02:43.253:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 17:02:44.259:handlerClient err3=EOF
2019/05/30 17:02:44.259:handlerClient Status=1

go服務(wù)器打印(windows):

2019/05/30 17:03:21.963:cur goos=windows
2019/05/30 17:03:21.976:server wait...
2019/05/30 17:03:31.903:new client conn=&{{0xc00008c2c0}},ip=127.0.0.1:57090
2019/05/30 17:03:31.922:read client [14]=Hi,here is glp
2019/05/30 17:03:31.922:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 17:03:32.923:handlerClient err3=EOF
2019/05/30 17:03:32.923:handlerClient Status=1

c++服務(wù)器打印(windows):

2019-05-30 17:06:08.737:服務(wù)器創(chuàng)建網(wǎng)絡(luò)描述符 fd=644
2019-05-30 17:06:08.737:服務(wù)器設(shè)置地址重用成功
2019-05-30 17:06:08.737:服務(wù)器綁定地址成功
2019-05-30 17:06:08.737:服務(wù)器監(jiān)聽成功
2019-05-30 17:06:29.437:客戶端新連接:接入 fd = 604,ip = 127.0.0.1
2019-05-30 17:06:29.449:client[604] recv[14]=Hi,here is glp
2019-05-30 17:06:29.450:send to client[604]=from server back[c++]:Hi,here is glp
2019-05-30 17:06:30.451:客戶端連接被主動關(guān)閉

經(jīng)過這步實驗證明確實是因為客戶端沒有讀取服務(wù)器發(fā)來的數(shù)據(jù)就關(guān)閉導(dǎo)致服務(wù)器的read或者recv報錯了省骂。

經(jīng)過調(diào)試我發(fā)現(xiàn)我可以通過如下步驟找到對應(yīng)的error

if err1, ok := err.(*net.OpError); ok {
    if err2, ok := err1.Err.(*os.SyscallError); ok {
        if err3, ok := err2.Err.(syscall.Errno); ok {
            if err3 == syscall.WSAECONNRESET {
                Log("err3 == syscall.WSAECONNRESET True\n")
            }
        }
    }
}

go定義的Errno

但是syscall.WSAECONNRESET不能用于linux蟀淮,在編譯linux的時候會報錯:

src\study\testServer.go:68:18: undefined: syscall.WSAECONNRESET

如果想要把沒有讀取數(shù)據(jù)的強(qiáng)制關(guān)閉也要歸結(jié)為close而不是error的時候,用上面的辦法只能用于windows钞澳。而我需要跨平臺解決方案怠惶。
在提出最后的解決方案之前,我們還有一個實驗需要做一下就是:

socket 10053

實驗5 socket出現(xiàn)10053的原因

改寫我們的go客戶端:

Log("connected ...\n")
hello := "Hi,here is glp"
n, err := conn.Write([]byte(hello))
if err != nil {
    Log("err4=%v\n", err.Error())
    return
}
Log("send server [%d]=%s\n", n, hello)

//buffer := make([]byte, 1024)
//n, err = conn.Read(buffer)
//if err != nil {
//  Log("err5=%v\n", err.Error())
//  return
//}
//Log("read server [%d]=%s\n", n, string(buffer))
//
//<-time.After(time.Second)

把讀取和等待的代碼都注釋了轧粟,查看服務(wù)器打硬咧巍:
go服務(wù)器打印(linux):

2019/05/30 17:26:09.186:cur goos=linux
2019/05/30 17:26:09.187:server wait...
2019/05/30 17:26:12.750:new client conn=&{{0xc0000b4080}},ip=115.192.79.216:57420
2019/05/30 17:26:12.760:read client [14]=Hi,here is glp
2019/05/30 17:26:12.760:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 17:26:12.760:handlerClient err3=EOF
2019/05/30 17:26:12.760:handlerClient Status=1

go服務(wù)器打印(windows):

2019/05/30 17:25:42.506:cur goos=windows
2019/05/30 17:25:42.520:server wait...
2019/05/30 17:25:48.386:new client conn=&{{0xc00008c2c0}},ip=127.0.0.1:57409
2019/05/30 17:25:48.400:read client [14]=Hi,here is glp
2019/05/30 17:25:48.400:send client [39]=from server back【go】:Hi,here is glp
2019/05/30 17:25:48.400:handlerClient err3=read tcp 127.0.0.1:20003->127.0.0.1:57409: wsarecv: An established connection was aborted by the software in your host machine.
2019/05/30 17:25:48.400:handlerClient Status=3

c++服務(wù)器打印(windows):

2019-05-30 17:26:23.847:服務(wù)器創(chuàng)建網(wǎng)絡(luò)描述符 fd=628
2019-05-30 17:26:23.847:服務(wù)器設(shè)置地址重用成功
2019-05-30 17:26:23.847:服務(wù)器綁定地址成功
2019-05-30 17:26:23.848:服務(wù)器監(jiān)聽成功
2019-05-30 17:26:53.961:客戶端新連接:接入 fd = 640,ip = 127.0.0.1
2019-05-30 17:26:53.973:client[640] recv[14]=Hi,here is glp
2019-05-30 17:26:53.973:send to client[640]=from server back[c++]:Hi,here is glp
2019-05-30 17:26:53.973:client[640] recv err=10053

筆者多實驗了幾次,windows偶爾會出現(xiàn)10054兰吟,但是以10053居多通惫。而linux一直是EOF。
看來這個錯誤碼只出現(xiàn)在windows平臺混蔼。情況是:服務(wù)器向客戶端發(fā)送數(shù)據(jù)的時候客戶端已經(jīng)close了履腋。
Linux應(yīng)該把這種情況當(dāng)EOF處理了。

解決方案:屏蔽強(qiáng)制關(guān)閉的錯誤

有時候我們服務(wù)器需要對socket的錯誤的原因進(jìn)行分析惭嚣,但是像這種客戶端直接close的遵湖,我們就不需要分析了,最好在日志層面就把它過濾掉晚吞。
但是我們又不能像上面提到的直接用常量延旧。這樣我們就沒辦法編譯跨平臺的服務(wù)器,至少要去改代碼才行槽地。而筆者想要的就是不動源碼的基礎(chǔ)上分辨是否是強(qiáng)制關(guān)閉迁沫。考慮良久闷盔,雖然不是很好但是比較實用的go代碼:

n, err := conn.Read(buffer)
if err != nil {
    Log("handlerClient err3=%v\n", err.Error())
    errStr := err.Error()
    err1 := err.(*net.OpError)
    if err == io.EOF || (runtime.GOOS == "windows" &&
        strings.Contains(errStr, "An existing connection was forcibly closed by the remote host") ||
        strings.Contains(errStr, "An established connection was aborted by the software in your host machine")) ||
        strings.Contains(errStr, "connection reset by peer") {
        /*
            1.io.EOF
                正常關(guān)閉.指客戶端讀完服務(wù)器發(fā)送的數(shù)據(jù)然后close

            2.
            connection reset by peer(linux)
            An existing connection was forcibly closed by the remote host(windows)
                表示客戶端 【沒有讀取/讀取部分】就close

            3.An established connection was aborted by the software in your host machine(windows)
                表示服務(wù)器發(fā)送數(shù)據(jù)弯洗,客戶端已經(jīng)close,這個經(jīng)過測試只有在windows上才會出現(xiàn)。linux試了很多遍都是返回io.EOF錯誤
                解決辦法就是客戶端發(fā)送數(shù)據(jù)的時候需要wait一下逢勾,然后再close,這樣close的結(jié)果就是2了
        */
        conn.Status = StatusClosed
    } else if err1 != nil && err1.Timeout() {
        conn.Status = StatusTimeout
    } else {
        conn.Status = StatusError
    }
    return
}

go服務(wù)器完整代碼:

package main

import (
    "fmt"
    "io"
    "net"
    "runtime"
    "strings"
    "time"
)

const (
    TimeFmt = "2006/01/02 15:04:05.000" //毫秒保留3位有效數(shù)字
)

func Log(format string, args ...interface{}) {
    fmt.Printf(fmt.Sprintf("%s:%s", time.Now().Format(TimeFmt), format), args...)
}

type StatusNO int32

const (
    StatusNormal  StatusNO = iota //0 正常
    StatusClosed                  //1 對方主動關(guān)閉
    StatusTimeout                 //2 超時連接斷開
    StatusError                   //3 其他異常斷開
)

type StatusConn struct {
    net.Conn
    //StatusNormal
    Status StatusNO
}

func handlerClient(conn *StatusConn) {
    defer func() {
        Log("handlerClient Status=%v\n", conn.Status)
        err := conn.Close()
        if err != nil {
            Log("handlerClient err1=%v\n", err)
        }
    }()

    for {
        buffer := make([]byte, 1024)
        err := conn.SetReadDeadline(time.Now().Add(time.Second * 10))
        if err != nil {
            Log("handlerClient err2=%v\n", err.Error())
            conn.Status = StatusError
            return
        }
        n, err := conn.Read(buffer)
        if err != nil {
            Log("handlerClient err3=%v\n", err.Error())
            errStr := err.Error()
            err1 := err.(*net.OpError)
            if err == io.EOF || (runtime.GOOS == "windows" &&
                strings.Contains(errStr, "An existing connection was forcibly closed by the remote host") ||
                strings.Contains(errStr, "An established connection was aborted by the software in your host machine")) ||
                strings.Contains(errStr, "connection reset by peer") {
                /*
                    1.io.EOF
                        正常關(guān)閉.指客戶端讀完服務(wù)器發(fā)送的數(shù)據(jù)然后close

                    2.
                    connection reset by peer(linux)
                    An existing connection was forcibly closed by the remote host(windows)
                        表示客戶端 【沒有讀取/讀取部分】就close

                    3.An established connection was aborted by the software in your host machine(windows)
                        表示服務(wù)器發(fā)送數(shù)據(jù)藐吮,客戶端已經(jīng)close,這個經(jīng)過測試只有在windows上才會出現(xiàn)溺拱。linux試了很多遍都是返回io.EOF錯誤
                        解決辦法就是客戶端發(fā)送數(shù)據(jù)的時候需要wait一下,然后再close谣辞,這樣close的結(jié)果就是2了
                */
                conn.Status = StatusClosed
            } else if err1 != nil && err1.Timeout() {
                conn.Status = StatusTimeout
            } else {
                conn.Status = StatusError
            }
            return
        }

        clientBytes := buffer[:n]
        Log("read client [%d]=%s\n", n, string(clientBytes))
        hello := fmt.Sprintf("from server back【go】:%s", string(clientBytes))

        n, err = conn.Write([]byte(hello))
        Log("send client [%d]=%s\n", n, hello)
        if err != nil {
            Log("handlerClient err4=%v\n", err.Error())
        }

    }
}

func main() {
    //服務(wù)器例子
    Log("cur goos=%s\n", runtime.GOOS)

    //獲取服務(wù)器監(jiān)聽ip地址
    ip, err := net.ResolveTCPAddr("", ":20003")
    if err != nil {
        Log("err1=%v\n", err.Error())
        return
    }

    //創(chuàng)建一個監(jiān)聽的socket
    conn, err := net.ListenTCP("tcp", ip)
    if err != nil {
        Log("err2=%v\n", err.Error())
        return
    }

    Log("server wait...\n")
    for {
        clientConn, err := conn.Accept()
        if err != nil {
            Log("err3=%v\n", err.Error())
            return
        }
        Log("new client conn=%v,ip=%v\n", clientConn, clientConn.RemoteAddr())
        go handlerClient(&StatusConn{clientConn, StatusNormal})
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迫摔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泥从,更是在濱河造成了極大的恐慌句占,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躯嫉,死亡現(xiàn)場離奇詭異纱烘,居然都是意外死亡杨拐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門擂啥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哄陶,“玉大人,你說我怎么就攤上這事哺壶∥荻郑” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵山宾,是天一觀的道長至扰。 經(jīng)常有香客問我,道長资锰,這世上最難降的妖魔是什么敢课? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮台妆,結(jié)果婚禮上翎猛,老公的妹妹穿的比我還像新娘。我一直安慰自己接剩,他們只是感情好切厘,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著懊缺,像睡著了一般疫稿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鹃两,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天遗座,我揣著相機(jī)與錄音,去河邊找鬼俊扳。 笑死途蒋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馋记。 我是一名探鬼主播号坡,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼梯醒!你這毒婦竟也來了宽堆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤茸习,失蹤者是張志新(化名)和其女友劉穎畜隶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡籽慢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年浸遗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗡综。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡乙帮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出极景,到底是詐尸還是另有隱情察净,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布盼樟,位于F島的核電站氢卡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏晨缴。R本人自食惡果不足惜译秦,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望击碗。 院中可真熱鬧筑悴,春花似錦、人聲如沸稍途。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽械拍。三九已至突勇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坷虑,已是汗流浹背甲馋。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留迄损,地道東北人定躏。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像芹敌,于是被迫代替她去往敵國和親共屈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348