使用net模塊實現(xiàn)基于TCP的數(shù)據(jù)通信

在Node.js中,提供了一個net模塊,專用于實現(xiàn)TCP服務(wù)器與TCP客戶端之間的通信。


1、創(chuàng)建TCP服務(wù)器

const server = net.createServer([options], [connectionListener]);

createServer方法返回被創(chuàng)建的TCP服務(wù)器,其中使用兩個可選參數(shù):

options:其值為一個對象,可以在該對象中使用一個布爾值類型的allowHalfOpen屬性,當該屬性值被指定為false時,當TCP服務(wù)器接收到客戶端發(fā)送的一個FIN包時將會回發(fā)一個FIN包,當該屬性值被設(shè)定為true時,當TCP服務(wù)器接收到客戶端發(fā)送的一個FIN包時不回發(fā)FIN包,這使得TCP服務(wù)器可以繼續(xù)向客戶端發(fā)送數(shù)據(jù),但是不會繼續(xù)接收客戶端發(fā)送的數(shù)據(jù)。開發(fā)者必須調(diào)用end方法來關(guān)閉socket連接。該屬性值默認為false。
connectionListener:用于指定當客戶端與服務(wù)器端建立連接時所要調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)使用一個參數(shù),參數(shù)值為該TCP服務(wù)器監(jiān)聽的socket端口對象。

function(socket) {
}

我們也可以不在createServer方法中使用connectionListener參數(shù),而是通過對connection事件進行監(jiān)聽,并且指定該事件的回調(diào)函數(shù)的方法來指定當客戶端與服務(wù)器建立連接時需要執(zhí)行的處理方法如下所示:

server.on('connection', function(socket) {
})

創(chuàng)建TCP服務(wù)器之后,可以使用listen方法通知服務(wù)器開始監(jiān)聽客戶端連接。該方法具有如下三種指定方法。
方法一:指定方法如下所示(代碼中server代表一個TCP服務(wù)器)

server.listen(port, [host], [backlog], [callback])

port:必須,用于指定需要監(jiān)聽的端口號,參數(shù)值為0時TCP服務(wù)器分配一個隨機端口號。
host:可選,用于指定需要監(jiān)聽的IP地址或主機名,如果省略該參數(shù),服務(wù)器將監(jiān)聽來自于任何IPv4地址的客戶端連接。
backlog:可選,參數(shù)值為一個整數(shù)值,用于指定位于等待隊列中的客戶端連接的最大數(shù)量,一旦超越這個長度,TCP服務(wù)器將開始拒絕來自于新的客戶端的連接請求,改參數(shù)的默認值為511。
callback:回調(diào)函數(shù),可選。該回調(diào)函數(shù)不使用任何參數(shù)。

方法二:指定方法如下所示(代碼中server代表一個使用Unix端口的服務(wù)器)

server.listen(path, [callback])

path:指定需要監(jiān)聽的路徑。
callback:回調(diào)函數(shù),可選。該回調(diào)函數(shù)不使用任何參數(shù)。
這種形式的listen方法用于通知一個使用Unix端口的服務(wù)器開始監(jiān)聽來自于指定路徑的客戶端連接。當對使用Unix端口的服務(wù)器指定了需要監(jiān)聽的路徑后,服務(wù)器將立即開始監(jiān)聽來自于該路徑的客戶端連接,這時觸發(fā)該服務(wù)器的listening事件,可使用listen方法的callback參數(shù)來指定listening事件觸發(fā)時調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。

方法三:指定方法如下所示(代碼中server代表一個TCP服務(wù)器)

server.listen(handle, [callback])

handle:指定需要監(jiān)聽的socket句柄。
callback:回調(diào)函數(shù),可選。該回調(diào)函數(shù)不使用任何參數(shù)。
這種形式的listen方法用于通知一個TCP服務(wù)器開始監(jiān)聽來自于指定socket句柄(該句柄可以為一個TCP服務(wù)器對象,可以為一個socket端口對象,也可以為一個文件描述符,在Windows操作系統(tǒng)中不支持對文件描述符的監(jiān)聽)的客戶端連接。當對TCP服務(wù)器指定了需要監(jiān)聽的socket句柄后,服務(wù)器端將立即開始監(jiān)聽來自于該socket句柄的客戶端連接,這時觸發(fā)該服務(wù)器的listening事件,可使用listen方法的callback參數(shù)來指定listening事件觸發(fā)時調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。

如果不在上述三種形式的listen方法中使用callback參數(shù),我們也可以通過監(jiān)聽TCP服務(wù)器對象的listening事件,并指定該事件觸發(fā)時調(diào)用的回調(diào)函數(shù)方法來指定TCP服務(wù)器開始監(jiān)聽時所需執(zhí)行的處理,方法如下所示:

server.on('listening', function(){
// 回調(diào)函數(shù)代碼略
})

在對TCP服務(wù)器指定需要監(jiān)聽的地址及端口時,如果該地址及端口已被占用,將產(chǎn)生一個錯誤代碼為“EADDRINUSE”的錯誤(表示用于監(jiān)聽的地址及端口已被占用),同時將觸發(fā)TCP服務(wù)器的error事件。

server.on('error', function(e) {
    if(e.code == 'EADDRINUSE') {
        // 回調(diào)函數(shù)代碼略
    }
})

2、TCP服務(wù)器擁有的屬性和方法

1)、使用TCP服務(wù)器的address方法,來查看服務(wù)器所監(jiān)聽的地址信息

let address = server.address()

該方法返回一個對象具有如下屬性:

  • port:屬性值為TCP服務(wù)器監(jiān)聽的socket端口號,如8080。
  • address:屬性值為TCP服務(wù)器監(jiān)聽的地址,如127.0.0.1。
  • family:屬性值為一個標識了TCP服務(wù)器所監(jiān)聽的地址是IPv4地址還是IPv6地址的字符串,如“IPv4”。

2)、使用TCP服務(wù)器的getConnections方法,來查看當前與TCP服務(wù)器建立連接的客戶端連接數(shù)量。

server.getConnections(callback)

該方法是一個異步方法,可以指定一個callback作為參數(shù),該回調(diào)函數(shù)使用兩個參數(shù):

err:參數(shù)值為獲取客戶端連接數(shù)時所觸發(fā)的錯誤對象。
count:參數(shù)值為獲取到的客戶端連接數(shù)。

3)、TCP服務(wù)器的maxConnections屬性,用于指定TCP服務(wù)器可以接收的客戶端連接的最大數(shù)量。

server.maxConnections = 2; //設(shè)置TCP服務(wù)器最大連接數(shù)為2

4)、使用TCP服務(wù)器的close方法,顯式指定服務(wù)器拒絕所有新的客戶端連接。

server.close(callback)

在使用close方法時,并不會斷開所有現(xiàn)存的客戶端連接。當這些客戶端連接被關(guān)閉時,TCP服務(wù)器將被自動關(guān)閉,同時觸發(fā)TCP服務(wù)器的close事件

server.on('close', function() {
}

3、socket端口對象

在Node.js中,使用net.Socket代表一個socket端口對象。該對象具有以下屬性和方法:

1)、使用socket端口對象的address方法,獲取該socket端口對象相關(guān)的地址信息

let address = server.address()

該方法返回一個對象具有如下屬性:

  • port:屬性值為TCP服務(wù)器監(jiān)聽的socket端口號,如8080。
  • address:屬性值為TCP服務(wù)器監(jiān)聽的地址,如127.0.0.1。
  • family:屬性值為一個標識了TCP服務(wù)器所監(jiān)聽的地址是IPv4地址還是IPv6地址的字符串,如“IPv4”。

socket端口對象可用來讀取客戶端發(fā)送的流數(shù)據(jù),每次接收到客戶端發(fā)送的流數(shù)據(jù)時觸發(fā)data事件。

socket.on('data', function(data){
})

在該回調(diào)函數(shù)中,使用一個參數(shù),參數(shù)值為一個Buffer對象(在未使用socket端口對象的setEncoding方法指定編碼方式時)或一個方法字符串對象(在使用socket端口對象的setEncoding方法指定編碼方式后)。我們可以使用如下所示方法設(shè)置讀取到數(shù)據(jù)的編碼格式:
方法一:使用socket端口對象的setEncoding方法。

socket.setEncoding('utf8');
socket.on('data', function(data) {
  console.log(data)
}

方法二:使用Buffer對象的toString方法。

socket.on('data', function(data) {
  console.log(data.toString())
}

2)、使用socket端口對象具有一個bytesRead屬性,屬性值為該socket端口對象接收到的客戶端發(fā)送數(shù)據(jù)的字節(jié)數(shù)。
當客戶端連接被關(guān)閉時觸發(fā)socket端口對象的end事件。可以通過對該事件進行監(jiān)聽并且指定事件處理函數(shù)來指定當客戶端連接被關(guān)閉時所需執(zhí)行的處理,該回調(diào)函數(shù)不使用任何參數(shù)。

const net = require('net');
let server = net.createServer();
server.on('connection', function(socket) {
    socket.setEncoding('utf8');
    socket.on('data', function(data) {
        console.log(data);
        console.log('已接收的字節(jié)數(shù)據(jù)', socket.bytesRead);
    });
    socket.on('end', function() {
        console.log('客戶端連接被關(guān)閉');
    });
})
server.listen(8080, 'locahost');

3)、可以使用socket對象的pipe方法,將客戶端發(fā)送的流數(shù)據(jù)書寫到文件等其他目標對象中。

socket.pipe(destination, [options])

該方法使用以下兩個參數(shù):

destination:參數(shù)值必須為一個可用于寫入流數(shù)據(jù)的對象。
options:可選,參數(shù)值為一個對象,可以在該對象中使用一個布爾類型的end屬性,如果該屬性值為true,則當數(shù)據(jù)被全部讀取完畢時立即結(jié)束寫操作;如果該屬性值為false,則并不結(jié)束寫操作,目標對象中可以被繼續(xù)寫入新的數(shù)據(jù),該屬性的默認屬性值為true。

const fs = require('fs');
const net = require('net');
let file = fs.createWriteStream('./message.txt');
let server = net.createServer();
server.on('connection', function(socket) {
    socket.pipe(file)
})
server.listen(8080, 'localhost')

4)、當使用了pipe方法指定要寫入的目標對象后,可以使用socket端口對象的unpipe方法取消目標對象的寫入操作。

socket.unpipe([destination])

該方法使用以下參數(shù):

destination:可選,參數(shù)值為pipe方法中指定的被寫入的目標對象。如果不使用該參數(shù),則取消所有對在pipe方法中指定的目標對象的寫入操作。

5)、使用socket端口對象的pause方法,可以暫停data事件的觸發(fā),這時服務(wù)器端將把每一個客戶端發(fā)送的數(shù)據(jù)暫存在一個單獨的緩存區(qū)中。

socket.pause();

6)、在使用了pause方法暫停data事件的觸發(fā)之后,可以使用socket端口對象的resume方法恢復(fù)data事件的觸發(fā),這時將讀取被緩存的該客戶端數(shù)據(jù)。

socket.resume()

7)、在Node.js中,未對客戶端連接指定默認超時時間,可以使用socket端口對象的setTimeout方法指定與該端口相連接的超時時間。

socket.setTimeou(timeout, [callback])

該方法使用以下參數(shù):

timeout:參數(shù)值為一個整數(shù)數(shù)值,用于指定客戶端連接的超時時間,單位為毫秒。
callback:可選,當客戶端連接超時時使用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。當客戶端連接超時時,觸發(fā)socket端口對象的timeout事件。我們也可以對timeout事件進行監(jiān)聽:

const fs = require('fs');
const net = require('net');
let file = fs.createWriteStream('./message.txt');
let server = net.createServer();
server.on('connection', function(socket) {
    socket.setTimeout(10*1000);
    socket.pause();
    socket.on('timeout', function() {
        socket.resume();
        socket.pipe(file);
    });
})
server.listen(8080, 'localhost')

在使用了一次setTimeout方法指定客戶端連接的超時時間后,可以通過再使用一次setTimeout方法并將timeout參數(shù)值指定為0的方法來取消對客戶端連接超時時間的指定。

4、創(chuàng)建TCP客戶端

在Node.js中,創(chuàng)建TCP客戶端只需要創(chuàng)建一個用于連接TCP服務(wù)器的socket端口對象即可。

const net = new net.socket([options])

在net.socket對象的構(gòu)造函數(shù)中,可以使用一個可選的options對象,其中可以使用如下屬性:

fd:用于指定一個現(xiàn)存的socket的文件描述符,TCP客戶端將使用這個現(xiàn)存的socket端口與服務(wù)器相連接。
type:用于指定客戶端所使用的協(xié)議,可指定值為“tcp4”、“tcp6”或者“unix”。
allowHalfOpen:該屬性值被指定為false時,當TCP服務(wù)器接收到客戶端發(fā)送的一個FIN包時將會回發(fā)一個FIN包,當該屬性值被設(shè)定為true時,當TCP服務(wù)器接收到客戶端發(fā)送的一個FIN包時不回發(fā)FIN包,這使得TCP服務(wù)器可以繼續(xù)向客戶端發(fā)送數(shù)據(jù),但是不會繼續(xù)接收客戶端發(fā)送的數(shù)據(jù)。開發(fā)者必須調(diào)用end方法來關(guān)閉socket連接。該屬性值默認為false。
創(chuàng)建了socket端口對象之后,即可使用socket端口對象的如下所示的兩種connect方法連接TCP服務(wù)器。

方法一:指定方法如下所示(代碼中socket代表一個socket端口對象)

socket.connect(port, [host], [connectListener])

port:必須,用于指定需要連接的TCP服務(wù)器端口。
host:可選,用于指定需要連接的TCP服務(wù)器地址,該地址可以是一個IP地址,也可以為一個主機名,如果不指定host參數(shù),將默認使用本地主機名localhost。
connectListener:可選,該參數(shù)用于指定一個當客戶端與TCP服務(wù)器成功連接時調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。
當客戶端與TCP服務(wù)器建立連接后,觸發(fā)socket端口對象的connect事件,我們也可以通過對connect事件的監(jiān)聽來執(zhí)行相關(guān)處理:

socket.on('connect', function(){
})

方法二:指定方法如下所示(代碼中socket代表一個socket端口對象)

socket.connect(patch,  [connectListener])

這種形式的connect方法用于與一個使用unix端口的服務(wù)器進行連接,方法中使用以下參數(shù):

path:必須,用于指定服務(wù)器所使用的unix端口的路徑。
connectListener:可選,該參數(shù)用于指定一個當客戶端與TCP服務(wù)器成功連接時調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。

當TCP客戶端與TCP服務(wù)器建立連接后,TCP客戶端用于建立連接的socket端口對象與TCP服務(wù)器端用于監(jiān)聽客戶端連接的socket端口對象都具有如下所示的屬性:

  • remoteAddress:連接的另一端所使用的遠程地址,例如74.125.127.X。
  • remotePort:連接的另一端所使用的端口號,例如80。
  • localAddress:本地用于建立連接的地址,例如192.168.1.1。
  • localPort:本地用于建立連接的端口號,例如80。

socket端口對象也可被用來寫入向客戶端或服務(wù)器端發(fā)送的流數(shù)據(jù),當流數(shù)據(jù)被寫入后將立即被發(fā)送到客戶端或服務(wù)器端。當需要寫入流數(shù)據(jù)時,可以使用write方法。

socket.write(data,  [encoding],  [callback])

該方法使用以下參數(shù):

data:參數(shù)值可以為一個Buffer對象或一個字符串,用于指定需要寫入的數(shù)據(jù)。
encoding:可選,用于指定當data參數(shù)為字符串時以什么編碼方式寫入。
callback:可選,用來指定當數(shù)據(jù)被寫入完畢時所要調(diào)用的回調(diào)函數(shù),該回調(diào)函數(shù)不使用任何參數(shù)。

在一個快速的網(wǎng)絡(luò)中,當數(shù)據(jù)量較少的時候,Node.js總是將數(shù)據(jù)直接發(fā)送到操作系統(tǒng)專用于發(fā)送數(shù)據(jù)的TCP緩存區(qū)中,然后從該TCP緩存區(qū)中取出數(shù)據(jù)發(fā)送給對方。在一個慢速的網(wǎng)絡(luò)中或需要發(fā)送大量數(shù)據(jù)時,TCP客戶端或服務(wù)器端所發(fā)送的數(shù)據(jù)并不一定會立即被對方接收,在這種情況下,Node.js會將這些數(shù)據(jù)緩存在緩存隊列中,在對方可以接收數(shù)據(jù)的情況下將緩存隊列中的數(shù)據(jù)通過TCP緩存區(qū)發(fā)送給對方。socket端口對象的write方法返回一個布爾類型的返回值,當數(shù)據(jù)直接被發(fā)送的TCP緩存區(qū)中時,改返回值為true,當數(shù)據(jù)直接被發(fā)送到緩存隊列時,該返回值為false。當返回值為false且TCP緩存區(qū)中數(shù)據(jù)已全部被發(fā)送出去時,觸發(fā)drain事件。
可以使用socket端口對象的bufferSize屬性值來查看用于緩存隊列中當前緩存的字符數(shù)。

創(chuàng)建TCP客戶端

創(chuàng)建TCP服務(wù)器

在TCP服務(wù)器與TCP客戶端建立連接或進行通信的過程中發(fā)生錯誤時,將觸發(fā)TCP服務(wù)器使用的與該客戶端相連接的socket端口對象或TCP客戶端使用的與服務(wù)器端相連接的socket端口對象的error事件。

socket.on('error', function(err){
})

在捕捉到錯誤之后,我們應(yīng)該使用觸發(fā)觸發(fā)錯誤的socket端口對象的destroy方法銷毀該socket端口對象,以確保該socket端口對象不會再被利用。

socket.destroy()

當TCP服務(wù)器與TCP客戶端建立連接后,TCP服務(wù)器可以使用與該客戶端相連接的socket端口對象的end方法關(guān)閉該客戶端的連接,TCP客戶端也可以使用與服務(wù)器端相連接的socket端口對象的end方法關(guān)閉與服務(wù)器端的連接。

socket.end([data], [encoding])

data:可選,其參數(shù)值可以為一個Buffer對象或一個字符串,用于指定在關(guān)閉連接前需要向另一端(客戶端或服務(wù)器端)追加發(fā)送的數(shù)據(jù)。
encoding:可選,用于指定當data參數(shù)為字符串時以什么編碼方式進行發(fā)送。

另外可以通過對end事件的監(jiān)聽,來執(zhí)行斷開連接后要執(zhí)行的操作。

socket.on('end', function() {
})

在默認情況下,運行TCP服務(wù)器的應(yīng)用程序不會自動退出,即使客戶端連接已被全部關(guān)閉。我們可以使用TCP服務(wù)器的unref方法指定當客戶端連接被全部關(guān)閉時退出應(yīng)用程序。

server.unref()

在使用了TCP服務(wù)器的unref方法之后,可以繼續(xù)使用TCP服務(wù)器的ref方法繼續(xù)阻止應(yīng)用程序的退出。

server.ref()

當socket端口徹底關(guān)閉時,觸發(fā)socket端口對象的close事件,可以通過對close事件的監(jiān)聽并且指定事件函數(shù)的方法來指定當socket端口關(guān)閉時需要執(zhí)行的處理。

socket.on('close', function(had_error) {
})

had_error:參數(shù)值為布爾類型,當參數(shù)值為true時,表示該socket端口的關(guān)閉是由于一個錯誤而引起的,當參數(shù)值為false時,表示該socket端口被正常關(guān)閉。

在TCP客戶端與服務(wù)器端建立連接后,當一方主機突然出現(xiàn)斷電、重啟、系統(tǒng)崩潰等意外情況時,將來不及向另一方發(fā)送用于關(guān)閉連接的FIN包,這樣另一方將永遠處于連接狀態(tài)。
在Node.js中,可以通過使用用于建立客戶端連接或服務(wù)器端連接的socket對象的setKeepAlive方法來解決這一問題。

socket.setKeepAlive([enable], [initialDelay])

enable:可選,參數(shù)值為布爾類型,當參數(shù)值為true時,啟用Keep-alive機制。啟用Keep-alive機制后,使用了setKeepAlive方法的這一段會不斷的向?qū)Ψ桨l(fā)送一個探測包,如果發(fā)送探測包后對方?jīng)]有發(fā)回響應(yīng)的話,則認為對方已關(guān)閉連接,執(zhí)行對方關(guān)閉連接時應(yīng)該執(zhí)行的處理,如果該參數(shù)為false,則不啟用Keep-alive機制,該屬性值默認為false,即不采用Keep-alive機制。
initialDelay:可選,指定每個多久發(fā)送一次探測包,單位為毫秒,如果將改參數(shù)設(shè)置為0,則保持系統(tǒng)中的默認設(shè)置的Keep-alive探測包的發(fā)送間隔時間,或者保持程序中當前已設(shè)置的Keep-alive探測包的發(fā)送間隔時間,該參數(shù)默認為0。

5、 net模塊中的類方法

  1. isIP方法
    isIP方法用于判斷一個字符串值是否為一個IP地址。
net.isIP(input)

input:該參數(shù)為一個字符串,如果字符串值不為IP地址,方法返回0,如果字符串值為IPv4地址,方法返回4;如果字符串值為IPv6地址,方法返回6。

  1. isIPv4方法
    isIPv4方法用于判斷一個字符串值是否為一個isIPv4地址。
net.isIPv4(input)

input:該參數(shù)為一個字符串,如果字符串值為一個有效的isIPv4地址,則方法返回true,否則返回false。

  1. isIPv6方法
    isIPv6方法用于判斷一個字符串值是否為一個isIPv6地址。
net.isIPv6(input)

input:該參數(shù)為一個字符串,如果字符串值為一個有效的isIPv6地址,則方法返回true,否則返回false。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

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