Socket.io

Socket.io提供了基于事件的實時雙向通訊

Browser和WebServer間的實時數(shù)據(jù)傳輸是一個很重要的需求,但最早只能通過AJAX輪詢方式實現(xiàn)。在WebSocket標準沒有推出之前鸦泳,AJAX輪詢是一種可行的方案状共。

AJAX輪詢原理是設(shè)置定時器,定時通過AJAX同步服務(wù)端數(shù)據(jù)勾徽。這種方式存在延時且對服務(wù)端造成很大負載。直至2011年,IETF才標準化WebSocket - 一種基于TCP套接字進行收發(fā)數(shù)據(jù)的協(xié)議泵三。

Socket.io將數(shù)據(jù)傳輸部分獨立出來形成engine.io,engine.io對WebSocket和AJAX輪詢進行了封裝衔掸,形成了一套API烫幕,屏蔽了細節(jié)差異和兼容性問題,實現(xiàn)了跨瀏覽器/跨設(shè)備進行雙向數(shù)據(jù)通信敞映。

WebSocket 協(xié)議

WebSocket是HTML5新增的一種通信協(xié)議较曼,其特點是服務(wù)端可以主動向客戶端推送信息,客戶端也可以主動向服務(wù)端發(fā)送信息振愿,是真正的雙向平等對話捷犹,屬于服務(wù)器推送技術(shù)的一種。

在WebSocket API中冕末,瀏覽器和服務(wù)器只需要做一個握手的動作萍歉,然后瀏覽器和服務(wù)端之間就形成了一條快速通道,兩者之間就直接可以數(shù)據(jù)相互傳送档桃,帶來的好處是

  1. 相互溝通的Header很小枪孩,大概只有2Bytes。
  2. 服務(wù)器不再被動的接收到瀏覽器的請求之后才返回數(shù)據(jù),而是在有新數(shù)據(jù)時就主動推送給瀏覽器蔑舞。

為了建立一個WebSocket連接拒担,瀏覽器首先要向服務(wù)器發(fā)起一個HTTP請求,這個請求和通常的HTTP請求不同斗幼,包含了一些附加頭信息澎蛛,其中附加頭信息Upgrade: WebSocket表明這是一個申請協(xié)議升級的HTTP請求。服務(wù)端解析這些頭信息蜕窿,然后產(chǎn)生應(yīng)答信息返回給客戶端谋逻,客戶端和服務(wù)端的WebSocket連接就建立起來了。雙方就可以通過這個連接通道自由的傳遞信息桐经,并且這個連接會持續(xù)直到客戶端或者服務(wù)端的某一方主動關(guān)閉連接毁兆。

為什么要使用WebSocket呢?

Browser已經(jīng)支持HTTP協(xié)議阴挣,為什么還要開發(fā)一種新的WebSocket協(xié)議呢气堕?

我們知道HTTP協(xié)議是一種單向的網(wǎng)絡(luò)協(xié)議,在建立連接后畔咧,僅允許Browser/UserAgent向WebServer發(fā)出請求資源后茎芭,WebServer才能返回對應(yīng)的數(shù)據(jù),而WebServer不能主動的推送數(shù)據(jù)給Browser/UserAgent誓沸。

最初這么設(shè)計HTTP協(xié)議的原因是梅桩,假設(shè)WebServer能主動的推送數(shù)據(jù)給Browser/UserAgent,那么Browser/UserAgent就太容易受到攻擊了拜隧,一些廣告商也會主動把廣告在不經(jīng)意間強行的傳輸給客戶端宿百,這不能不說是一個災(zāi)難。那么單向的HTTP協(xié)議給Web應(yīng)用開發(fā)帶哪些問題呢洪添?

現(xiàn)在假設(shè)我們要開發(fā)一個基于Web的應(yīng)用去獲取當前WebServer的實時數(shù)據(jù)垦页。例如股票實時行情、火車票剩余票數(shù)等干奢。這就需要Browser/UserAgent與WebServer之間反復(fù)進行HTTP通信痊焊,Browser/UserAgent不斷的發(fā)送請求去獲取當前的實時數(shù)據(jù)。

常見的方式

  • Polling

Polling輪詢是通過Browser/UserAgent定時向WebServer發(fā)送HTTP請求忿峻,WebServer收到請求后把最新的數(shù)據(jù)發(fā)回給Browser/UserAgent宋光,Browser/UserAgent得到數(shù)據(jù)后將其顯示,然后再定期重復(fù)此過程炭菌。

雖然這樣可以滿足需求罪佳,但仍存在問題,例如某段時間內(nèi)WebServer沒有更新的數(shù)據(jù)黑低,但Browser/UserAgent仍然會定時發(fā)送請求過來詢問赘艳,WebServer可以把以前的老數(shù)據(jù)再傳送過去酌毡,Browser/UserAgent把這些沒有變化的數(shù)據(jù)再顯示出來。這樣既浪費網(wǎng)絡(luò)帶寬蕾管,有浪費CPU利用率枷踏。

如果說把Browser/UserAgent發(fā)送請求的周期調(diào)大一些,就可以緩解這個問題掰曾,但如果WebServer的數(shù)據(jù)更新很快時旭蠕,這樣又不能保證Web應(yīng)用獲取數(shù)據(jù)的實時性。

  • LongPolling

LongPolling是對Polling的一種改進旷坦。

Browser/UserAgent發(fā)送HTTP請求到WebServer掏熬,此時WebServer可以做2件事情:

  1. 如果WebServer有新的數(shù)據(jù)需要傳送,就立即把數(shù)據(jù)發(fā)回給Browser/UserAgent秒梅,Browser/UserAgent收到數(shù)據(jù)后旗芬,立即再發(fā)送HTTP請求給WebServer。

  2. 如果WebServer沒有新數(shù)據(jù)需要傳送捆蜀,這里與Polling的方式不同的是疮丛,WebServer不是立即發(fā)送回應(yīng)給Browser/UserAgent,而是將這個請求保持住辆它,等待有新的數(shù)據(jù)來到誊薄,再去響應(yīng)這個請求。當然锰茉,如果WebServer的數(shù)據(jù)長期沒有更新暇屋,一段時間后,這個HTTP請求就會超時洞辣,Browser/UserAgent收到超時信息后,在立即發(fā)送一個新的HTTP請求給服務(wù)器昙衅,然后依次循環(huán)這個過程扬霜。

LongPolling的方式雖然在某種程度上減少了網(wǎng)絡(luò)帶寬和CPU利用率等問題,但仍存在缺陷而涉。

例如WebServer的數(shù)據(jù)更新速度較快著瓶,WebServer在傳送一個數(shù)據(jù)包給Browser/UserAgent后必須等待Browser的下一個HTTP請求到來,才能傳遞第二個更新的數(shù)據(jù)包給Browser啼县。這樣的話材原,Browser顯示實時數(shù)據(jù)最快的時間為2 xRTT(往返時間)。另外在網(wǎng)絡(luò)擁堵的情況下季眷,這個應(yīng)該是不能讓用戶接受的余蟹。另外,由于HTTP數(shù)據(jù)包的頭部數(shù)據(jù)量很大(通常有400多個字節(jié))子刮,但真正被服務(wù)器需要的數(shù)據(jù)卻很少(有時只有10個字節(jié)左右)威酒,這樣的數(shù)據(jù)包在網(wǎng)絡(luò)上周期性傳輸窑睁,難免對網(wǎng)絡(luò)帶寬是一種浪費。

綜上所述葵孤,要是在Browser有一種新的網(wǎng)路一些担钮,能支持客戶端和服務(wù)端的雙向通信,而且協(xié)議的頭部又不那么龐大就very nice了尤仍。WebSocket正是肩負這樣的使命登上了Web的舞臺箫津。

WebSocket 原理

WebSocket是一種雙向通信協(xié)議,它建立在TCP之上宰啦,同HTTP一樣通過TCP來傳輸數(shù)據(jù)苏遥,但與HTTP最大不同的是:

  1. WebSocket是一種雙向通信協(xié)議,在建立連接后绑莺,WebSocket服務(wù)器和Browser/UserAgent都能主動的向?qū)ο蟀l(fā)送或接收數(shù)據(jù)暖眼,就像Socket一樣,不同的是WebSocket是一種建立在Web基礎(chǔ)上的簡單模擬Socket的協(xié)議纺裁。

  2. WebSocket需要通過握手連接诫肠,類似TCP也需要客戶端和服務(wù)端進行握手連接,連接成功后才能相互通信欺缘。

建立握手的時序圖

簡單說明下WebSocket握手的過程

當Web應(yīng)用端調(diào)用new WebSocket(url)接口時栋豫,Browser就開始了與地址為URL的WebServer建立握手連接的過程。

  1. Browser與WebSocket服務(wù)器通過TCP三次握手建立連接谚殊,如果這個建立連接失敗丧鸯,那么后面的過程就不會執(zhí)行,Web應(yīng)用將收到錯誤消息通知嫩絮。

  2. 在TCP建立連接成功后丛肢,Browser/UserAgent通過HTTP協(xié)議傳送WebSocket支持的版本號、協(xié)議的字版本號剿干、原始地址蜂怎、主機地址等一系列字段給服務(wù)端。

  3. WebSocket服務(wù)器收到Browser/UserAgent發(fā)送來的握手請求后置尔,如果數(shù)據(jù)包數(shù)據(jù)和格式正確杠步,客戶端和服務(wù)端的協(xié)議版本匹配等,就接受本次握手連接榜轿,并給出對應(yīng)的數(shù)據(jù)回復(fù)幽歼,同樣回復(fù)的數(shù)據(jù)包也是采用HTTP協(xié)議傳輸。

  4. Browser收到服務(wù)器回復(fù)的數(shù)據(jù)包后谬盐,如果數(shù)據(jù)包內(nèi)容甸私、格式都沒有問題的話,就表示本次連接成功飞傀,觸發(fā)onopen消息颠蕴,此時Web開發(fā)者就可以在此時通過send接口向服務(wù)器發(fā)送數(shù)據(jù)泣刹。否則,握手連接失敗犀被,Web應(yīng)用會收到onerror消息椅您,并且能知道連接失敗的原因。

WebSocket與TCP寡键、HTTP的關(guān)系

WebSocket與HTTP協(xié)議一樣都是基于TCP的掀泳,所以它們都是可靠的協(xié)議,Web開發(fā)者調(diào)用的WebSocketsend函數(shù)在Browser的實現(xiàn)中最終都是通過TCP的系統(tǒng)接口進行傳輸?shù)摹?/p>

WebSocket和HTTP協(xié)議樣都屬于應(yīng)用層協(xié)議西轩,那么它們之間有沒有什么關(guān)系呢员舵?

答案是肯定的,WebSocket在建立握手連接時藕畔,數(shù)據(jù)是通過HTTP協(xié)議傳輸?shù)穆砥А5诮⑦B接之后,真正的數(shù)據(jù)傳輸階段是不需要HTTP參與的注服。

WebSocket與TCP和HTTP的關(guān)系

WebSocket Server

如果要搭建一個WebServer韭邓,我們會有很多選擇,市場上也有很多成熟的產(chǎn)品供我們是使用溶弟。例如開源的Apache女淑,安裝配置后即可工作。但如果想要搭建一個WebSocket服務(wù)器就沒有那么輕松辜御,因為WebSocket是一種新的通信協(xié)議鸭你,目前還是草案,沒有成為標準擒权,市場上也沒有成熟的WebSocket服務(wù)器或Library實現(xiàn)WebSocket協(xié)議袱巨,我們必須自己手動編碼去解析和組裝WebSocket的數(shù)據(jù)包。要完成一個WebSocket服務(wù)器碳抄,估計所有的人都想放棄愉老,不過市場上有幾款比較好的開源Library可供使用。例如PyWebSocket纳鼎、WebSocket-Node、LibWebSockets等裳凸,這些Library已經(jīng)實現(xiàn)了WebSocket數(shù)據(jù)包的封裝和解析贱鄙,我們可以調(diào)用這些接口,這在很大程度上減少了我們的工作量姨谷。

Socket.io

socket.io是一個跨瀏覽器支持WebSocket的實時通訊的JS逗宁。

http://socket.io/docs/

由于HTTP是無狀態(tài)的協(xié)議,要實現(xiàn)即時通訊非常困難梦湘。因為當對方發(fā)送一條消息時瞎颗,服務(wù)器并不知道當前有哪些用戶等著接收消息件甥,當前實現(xiàn)即時通訊功能最為普遍的方式就是輪詢機制。即客戶端定期發(fā)起一個請求哼拔,看看有沒有人發(fā)送消息到服務(wù)器引有,如果有服務(wù)端就將消息發(fā)給客戶端。這種做法的缺點顯而易見倦逐,那么多的請求將消耗大量資源譬正,大量的請求其實是浪費的。

現(xiàn)在檬姥,我們有了WebSocket曾我,它是HTML5的新API。WebSocket連接本質(zhì)上就是建立一個TCP連接健民,WebSocket會通過HTTP請求建立抒巢,建立后的WebSocket會在客戶端和服務(wù)端建立一個持久的連接,直到有一方主動關(guān)閉該連接秉犹。所以蛉谜,現(xiàn)在服務(wù)器就知道有哪些用戶正在連接了,這樣通訊就變得相對容易了凤优。

Socket.io支持及時悦陋、雙向、基于事件的交流筑辨,可在不同平臺俺驶、瀏覽器、設(shè)備上工作棍辕,可靠性和速度穩(wěn)定暮现。最典型的應(yīng)用場景如:

  • 實時分析:將數(shù)據(jù)推送到客戶端,客戶端表現(xiàn)為實時計數(shù)器楚昭、圖表栖袋、日志客戶。
  • 實時通訊:聊天應(yīng)用
  • 二進制流傳輸:socket.io支持任何形式的二進制文件傳輸抚太,例如圖片塘幅、視頻、音頻等尿贫。
  • 文檔合并:允許多個用戶同時編輯一個文檔电媳,并能夠看到每個用戶做出的修改。

Socket.io實際上是WebSocket的父集庆亡,Socket.io封裝了WebSocket和輪詢等方法匾乓,會根據(jù)情況選擇方法來進行通訊。

Node.js提供了高效的服務(wù)端運行環(huán)境又谋,但由于Browser對HTML5的支持不一拼缝,為了兼容所有瀏覽器娱局,提供實時的用戶體驗,并為開發(fā)者提供客戶端與服務(wù)端一致的編程體驗咧七,于是Socket.io誕生了衰齐。

# npm安裝socket.op
$ npm install --save socket.io

Socket.io將WebSocket和Polling機制以及其它的實時通信方式封裝成通用的接口,并在服務(wù)端實現(xiàn)了這些實時機制相應(yīng)代碼猪叙。這就是說娇斩,WebSocket僅僅是Socket.io實現(xiàn)實時通信的一個子集,那么Socket.io都實現(xiàn)了Polling中那些通信機制呢穴翩?

  • Adobe Flash Socket
    大部分PC瀏覽器都支持的Socket模式犬第,不過是通過第三方嵌入到瀏覽器,不在W3C規(guī)范內(nèi)芒帕,可能將逐步被淘汰歉嗓。況且,大部分手機瀏覽器并不支持此種模式背蟆。
  • AJAX Long Polling
    定時向服務(wù)端發(fā)送請求鉴分,缺點是給服務(wù)端帶來壓力并出現(xiàn)信息更新不及時的現(xiàn)象。
  • AJAX multipart streaming
    在XMLHttpRequest對象上使用某些瀏覽器支持的multi-part標志带膀,AJAX請求被發(fā)送給服務(wù)端并保持打開狀態(tài)(掛起狀態(tài))志珍,每次需要向客戶端發(fā)送信息,就尋找一個掛起的HTTP請求響應(yīng)給客戶端垛叨,并且所有的響應(yīng)都會通過統(tǒng)一連接來寫入伦糯。
  • Forever Iframem
    永存的Iframe設(shè)計了一個置于頁面中隱藏的iframe標簽,該標簽的src屬性指向返回服務(wù)端時間的Servlet路徑嗽元。每次在事件到達時敛纲,Servlet寫入并刷新一個新的Script標簽,該標簽內(nèi)部帶有JS代碼剂癌,iframe的內(nèi)容被附加上script標簽淤翔,標簽中的內(nèi)容就會得到執(zhí)行。這種方式的缺點是接收數(shù)據(jù)都是由瀏覽器通過HTML標簽來處理的佩谷,因此無法知道連接何時在哪一端被斷開旁壮,而且iframe標簽在瀏覽器中將被逐步取消。
  • JSONP Polling
    JSONP輪詢基本與HTTP輪詢一樣谐檀,不同之處則是JSONP可發(fā)出跨域請求抡谐。

Socket.io 基本應(yīng)用

socket.io提供了基于事件的實時雙向通訊,它同時提供了服務(wù)端和客戶端的API稚补。

服務(wù)端

服務(wù)端socket.io必須綁定一個http.Server實例童叠,因為WebSocket協(xié)議是構(gòu)建在HTTP協(xié)議之上的框喳,所以在創(chuàng)建WebSocket服務(wù)時需調(diào)用HTTP模塊并調(diào)用其下createServer()方法课幕,將生成的server作為參數(shù)傳入socket.io厦坛。

var httpServer = require('http').createServer();
var io = require('socket.io')(httpServer);
httpServer.listen(3000);

綁定http.Server可使用隱式綁定和顯式綁定

  • 隱式綁定

socket.io內(nèi)部實例化并監(jiān)聽http.Server,通過實例化時傳入端口或者在實例化后調(diào)用listenattach函數(shù)進行隱式綁定乍惊。

// 實例化時傳入端口
require('socket.io')(3000)

// 通過listen或attach函數(shù)綁定
let io = require('socket.io')
io.listen(3000);
// io.attach(3000);
  • 顯式綁定
// 實例化時綁定
let httpServer = require('http').Server();
let io = require('socket.io')(httpServer);
httpServer.listen(3000);

//通過listen或attach綁定
let httpServer = require('http').Server();
let io = require('socket.io')();
io.listen(httpServer);
// io.attach(httpServer);
httpServer.listen(3000);

Express框架中使用

let app = require('express');

let httpServer= require('http').Server(app);
let io = require('socket.io')(httpServer);

app.listen(3000);

KOA框架中使用

let app = require('koa')();

let httpServer = require('http').Server(app.callback());
let io = require('socket.io')(httpServer);

app.listen(3000);

建立連接

當服務(wù)端和客戶端連接成功時杜秸,服務(wù)端會監(jiān)聽到connectionconnect事件,客戶端會監(jiān)聽到connect事件润绎,斷開連接時服務(wù)端對應(yīng)到客戶端的socket與客戶端均會監(jiān)聽到disconcect事件撬碟。

/*客戶端*/
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
<script>
// socket.io引入成功后,可通過io()生成客戶端所需的socket對象莉撇。
let socket = io('http://127.0.0.0:3000');

// socket.emit()用戶客戶端向服務(wù)端發(fā)送消息呢蛤,服務(wù)端與之對應(yīng)的是socket.on()來接收信息。
socket.emit('client message', {msg:'hi, server'});

// socket.on()用于接收服務(wù)端發(fā)來的消息
socket.on('connect',  ()=>{
  console.log('client connect server');
});
socket.on('disconnect', ()=>{
  console.log('client disconnect');
});
</script>

/*服務(wù)端*/
// 服務(wù)端綁定HTTP服務(wù)器實例
let httpServer = require('http').Server();
let io = require('socket.io')(httpServer);
httpServer.listen(3000);

// 服務(wù)端監(jiān)聽連接狀態(tài):io的connection事件表示客戶端與服務(wù)端成功建立連接棍郎,它接收一個回調(diào)函數(shù)其障,回調(diào)函數(shù)會接收一個socket參數(shù)。
io.on('connection',  (socket)=>{
  console.log('client connect server, ok!');

  // io.emit()方法用于向服務(wù)端發(fā)送消息涂佃,參數(shù)1表示自定義的數(shù)據(jù)名励翼,參數(shù)2表示需要配合事件傳入的參數(shù)
  io.emit('server message', {msg:'client connect server success'});

  // socket.broadcast.emit()表示向除了自己以外的客戶端發(fā)送消息
  socket.broadcast.emit('server message', {msg:'broadcast'});

  // 監(jiān)聽斷開連接狀態(tài):socket的disconnect事件表示客戶端與服務(wù)端斷開連接
  socket.on('disconnect', ()=>{
    console.log('connect disconnect');
  });
  
  // 與客戶端對應(yīng)的接收指定的消息
  socket.on('client message', (data)=>{
    cosnole.log(data);// hi server
  });

  socket.disconnect();
});

傳輸數(shù)據(jù)

服務(wù)端和客戶端的socket是一個關(guān)聯(lián)的EventEmitter對象,客戶端socket派發(fā)的事件可以通過被服務(wù)端的socket接收辜荠,服務(wù)端socket派發(fā)的事件也可以被客戶端接收汽抚。基于這種機制伯病,可以實現(xiàn)雙向交流造烁。

# 模擬:客戶端不斷發(fā)送隨機數(shù),當隨機數(shù)大于0.95時狱从,服務(wù)端延遲1s后向客戶端發(fā)送警告以及警告次數(shù)膨蛮。
/*客戶端*/
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
<script>
let socket = io('http://127.0.0.1:3000');

let interval = setTimeInterval(()=>{
  socket.emit('random', Math.random());
}, 500);

socket.on('warn', count=>{
  console.log('warning count : '+count);
});

socket.on('disconnect', ()=>{
  clearInterval(interval);
});
</script>


/*服務(wù)端*/
let httpServer = require('http').Server();
let io = require('socket.io')(httpServer);
httpServer.listen(3000);

io.on('connection', socket=>{
  socket.on('random', value=>{
    console.log(value);
    if(value>0.95){
      if(typeof socket.warnign==='undefined'){
        socket.warning = 0;// socket對象可用來存儲狀態(tài)和自定義數(shù)據(jù)
      }
      setTimeout(()=>{
        socket.emit('warn', ++socket.warning);
      }, 1000);
    }
  });
});

客戶端

運行環(huán)境:NodeJS

API https://www.w3cschool.cn/socket/socket-k49j2eia.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市季研,隨后出現(xiàn)的幾起案子敞葛,更是在濱河造成了極大的恐慌,老刑警劉巖与涡,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惹谐,死亡現(xiàn)場離奇詭異,居然都是意外死亡驼卖,警方通過查閱死者的電腦和手機氨肌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酌畜,“玉大人怎囚,你說我怎么就攤上這事。” “怎么了恳守?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵考婴,是天一觀的道長。 經(jīng)常有香客問我催烘,道長沥阱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任伊群,我火速辦了婚禮考杉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舰始。我一直安慰自己崇棠,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布丸卷。 她就那樣靜靜地躺著易茬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪及老。 梳的紋絲不亂的頭發(fā)上抽莱,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音骄恶,去河邊找鬼食铐。 笑死,一個胖子當著我的面吹牛僧鲁,可吹牛的內(nèi)容都是我干的虐呻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼寞秃,長吁一口氣:“原來是場噩夢啊……” “哼斟叼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起春寿,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤朗涩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绑改,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谢床,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年厘线,在試婚紗的時候發(fā)現(xiàn)自己被綠了识腿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡造壮,死狀恐怖渡讼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤成箫,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布栖雾,位于F島的核電站,受9級特大地震影響伟众,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜召廷,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一凳厢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧竞慢,春花似錦先紫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至败潦,卻和暖如春本冲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劫扒。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工檬洞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沟饥。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓添怔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親版述。 傳聞我的和親對象是個殘疾皇子踢代,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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