Java NIO——Selector選擇器

一薛窥、簡介

  • 1.1胖烛、Java 的 NIO眼姐,用非阻塞的 IO 方式∨宸可以用一個線程众旗,處理多個的客戶端連接,就會使用到Selector(選擇器)

  • 1.2趟畏、Selector 能夠檢測多個注冊的通道上是否有事件發(fā)生(注意:多個Channel以事件的方式可以注冊到同一個Selector)贡歧,如果有事件發(fā)生,便獲取事件然后針對每個事件進(jìn)行相應(yīng)的處理赋秀。這樣就可以只用一個單線程去管理多個通道利朵,也就是管理多個連接和請求。

  • 1.3猎莲、只有在 連接/通道 真正有讀寫事件發(fā)生時绍弟,才會進(jìn)行讀寫,就大大地減少了系統(tǒng)開銷著洼,并且不必為每個連接都創(chuàng)建一個線程樟遣,不用去維護(hù)多個線程。

  • 1.4身笤、避免了多線程之間的上下文切換導(dǎo)致的開銷豹悬。

Selector一般稱為選擇器 ,也稱多路復(fù)用器液荸,多條channel復(fù)用selector瞻佛。channe通過注冊到selector ,使selector對channel進(jìn)行監(jiān)聽娇钱,實現(xiàn)盡可能少的線程管理多個連接伤柄。減少了 線程的使用涡尘,降低了因為線程的切換引起的不必要額資源浪費(fèi)和多余的開銷。
也是網(wǎng)絡(luò)傳輸非堵塞的核心組件响迂。

二考抄、特點(diǎn)

  • 2.1、Netty 的 IO 線程 NioEventLoop 聚合了 Selector(選擇器蔗彤,也叫多路復(fù)用器)川梅,可以同時并發(fā)處理成百上千個客戶端連接。

  • 2.2然遏、當(dāng)線程從某客戶端 Socket 通道進(jìn)行讀寫數(shù)據(jù)時贫途,若沒有數(shù)據(jù)可用時,該線程可以進(jìn)行其他任務(wù)待侵。

  • 2.3丢早、線程通常將非阻塞 IO 的空閑時間用于在其他通道上執(zhí)行 IO 操作,所以單獨(dú)的線程可以管理多個輸入和輸出通道秧倾。

  • 2.4怨酝、由于讀寫操作都是非阻塞的,這就可以充分提升 IO 線程的運(yùn)行效率那先,避免由于頻繁 I/O 阻塞導(dǎo)致的線程掛起农猬。

  • 2.5、一個 I/O 線程可以并發(fā)處理 N 個客戶端連接和讀寫操作售淡,這從根本上解決了傳統(tǒng)同步阻塞 I/O 一連接一線程模型斤葱,架構(gòu)的性能、彈性伸縮能力和可靠性都得到了極大的提升揖闸。

三揍堕、Selector的作用

選擇器提供選擇執(zhí)行已經(jīng)就緒的任務(wù)的能力。從底層來看汤纸,Selector提供了詢問通道是否已經(jīng)準(zhǔn)備好執(zhí)行每個I/O操作的能力衩茸。Selector 允許單線程處理多個Channel。僅用單個線程來處理多個Channels的好處是蹲嚣,只需要更少的線程來處理通道递瑰。事實上,可以只用一個線程處理所有的通道隙畜,這樣會大量的減少線程之間上下文切換的開銷抖部。

四、Selector類相關(guān)方法

Selector 類是一個抽象類, 常用方法和說明如下:

public abstract class Selector implements Closeable {

    public static Selector open();//得到一個選擇器對象
    
    public abstract int select(long timeout);//監(jiān)控所有注冊的通道议惰,當(dāng)其中有 IO 操作可以進(jìn)行時慎颗,將
                                             //對應(yīng)的 SelectionKey 加入到內(nèi)部集合中并返回,參數(shù)用來設(shè)置超時時間
    public abstract Set<SelectionKey> selectedKeys();//從內(nèi)部集合中得到所有的 SelectionKey
}

注意事項:

  • 1、NIO中的 ServerSocketChannel功能類似ServerSocket俯萎,SocketChannel功能類似Socket傲宜。

  • 2、selector select()方法詳解
    select()方法夫啊,可以查詢出已經(jīng)就緒的通道操作函卒,這些就緒的狀態(tài)集合,保存在一個元素是SelectionKey對象的Set集合中撇眯。

    • selector.select()//阻塞
    • selector.select(1000);//阻塞1000毫秒报嵌,在1000毫秒后返回
    • selector.selectNow();//不阻塞,立馬返還
    • selector.wakeup();//喚醒selector

select()方法返回的int值熊榛,表示有多少通道已經(jīng)就緒锚国,更準(zhǔn)確的說,是自前一次select方法以來到這一次select方法之間的時間段上玄坦,有多少通道變成就緒狀態(tài)血筑。

NIO 非阻塞 網(wǎng)絡(luò)編程原理分析圖

NIO 非阻塞 網(wǎng)絡(luò)編程相關(guān)的(Selector、SelectionKey煎楣、ServerScoketChannel和SocketChannel) 關(guān)系梳理圖


  • 1豺总、當(dāng)客戶端連接時,會通過ServerSocketChannel 得到 SocketChannel转质。

  • 2园欣、Selector 進(jìn)行監(jiān)聽 select 方法, 返回有事件發(fā)生的通道的個數(shù)帖世。

  • 3休蟹、將socketChannel注冊到Selector上,register(Selector sel日矫,int ops)赂弓,一個selector上可以注冊多個SocketChannel。

  • 4哪轿、注冊后返回一個 SelectionKey, 會和該Selector 關(guān)聯(lián)(集合)盈魁。

  • 5、進(jìn)一步得到各個 SelectionKey (有事件發(fā)生)窃诉。

  • 6杨耙、在通過 SelectionKey 反向獲取 SocketChannel,方法 channel()飘痛。

  • 7珊膜、可以通過 得到的 channel,完成業(yè)務(wù)處理宣脉。

五漆枚、可選擇通道(SelectableChannel)

并不是所有的Channel呻袭,都是可以被Selector 復(fù)用的蚀同。比方說阻课,F(xiàn)ileChannel就不能被選擇器復(fù)用。

判斷一個Channel 能被Selector 復(fù)用习寸,有一個前提:判斷他是否繼承了一個抽象類SelectableChannel。如果繼承了SelectableChannel,則可以被復(fù)用感憾,否則不能。

SelectableChannel類提供了實現(xiàn)通道的可選擇性所需要的公共方法令花。它是所有支持就緒檢查的通道類的父類吹菱。所有socket通道,都繼承了SelectableChannel類都是可選擇的彭则,包括從管道(Pipe)對象的中獲得的通道鳍刷。而FileChannel類,沒有繼承SelectableChannel俯抖,因此是不是可選通道输瓜。

通道和選擇器注冊之后,他們是綁定的關(guān)系嗎芬萍?

不是一對一的關(guān)系尤揣。一個通道可以被注冊到多個選擇器上,但對每個選擇器而言只能被注冊一次柬祠。

通道和選擇器之間的關(guān)系北戏,使用注冊的方式完成。SelectableChannel可以被注冊到Selector對象上漫蛔,在注冊的時候嗜愈,需要指定通道的哪些操作,是Selector感興趣的莽龟。

Channel注冊到Selector

使用Channel.register(Selector sel蠕嫁,int ops)方法,將一個通道注冊到一個選擇器時毯盈。第一個參數(shù)剃毒,指定通道要注冊的選擇器是誰。第二個參數(shù)指定選擇器需要查詢的通道操作搂赋。

可以供選擇器查詢的通道操作赘阀,從類型來分,包括以下四種:

  • 1脑奠、可讀 : SelectionKey.OP_READ
  • 2基公、可寫 : SelectionKey.OP_WRITE
  • 3、連接 : SelectionKey.OP_CONNECT
  • 4捺信、接收 : SelectionKey.OP_ACCEPT

如果Selector對通道的多操作類型感興趣酌媒,可以用“位或”操作符來實現(xiàn):int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE ;

注意欠痴,操作一詞,是一個是使用非常泛濫秒咨,也是一個容易混淆的詞喇辽。特別提醒的是,選擇器查詢的不是通道的操作雨席,而是通道的某個操作的一種就緒狀態(tài)菩咨。

一旦通道具備完成某個操作的條件,表示該通道的某個操作已經(jīng)就緒陡厘,就可以被Selector查詢到抽米,程序可以對通道進(jìn)行對應(yīng)的操作。比方說糙置,某個SocketChannel通道可以連接到一個服務(wù)器云茸,則處于“連接就緒”(OP_CONNECT)。再比方說谤饭,一個ServerSocketChannel服務(wù)器通道準(zhǔn)備好接收新進(jìn)入的連接标捺,則處于“接收就緒”(OP_ACCEPT)狀態(tài)。還比方說揉抵,一個有數(shù)據(jù)可讀的通道亡容,可以說是“讀就緒”(OP_READ)。一個等待寫數(shù)據(jù)的通道可以說是“寫就緒”(OP_WRITE)冤今。

六闺兢、選擇鍵(SelectionKey)

Channel和Selector的關(guān)系確定好后,并且一旦通道處于某種就緒的狀態(tài)戏罢,就可以被選擇器查詢到屋谭。這個工作,使用選擇器Selector的select()方法完成帖汞。select方法的作用戴而,對感興趣的通道操作,進(jìn)行就緒狀態(tài)的查詢翩蘸。

Selector可以不斷的查詢Channel中發(fā)生的操作的就緒狀態(tài)。并且挑選感興趣的操作就緒狀態(tài)淮逊。一旦通道有操作的就緒狀態(tài)達(dá)成催首,并且是Selector感興趣的操作,就會被Selector選中泄鹏,放入選擇鍵集合中郎任。

一個選擇鍵,首先是包含了注冊在Selector的通道操作的類型备籽,比方說SelectionKey.OP_READ舶治。也包含了特定的通道與特定的選擇器之間的注冊關(guān)系分井。

開發(fā)應(yīng)用程序是,選擇鍵是編程的關(guān)鍵霉猛。NIO的編程尺锚,就是根據(jù)對應(yīng)的選擇鍵,進(jìn)行不同的業(yè)務(wù)邏輯處理惜浅。

選擇鍵的概念瘫辩,有點(diǎn)兒像事件的概念。

一個選擇鍵有點(diǎn)兒像監(jiān)聽器模式里邊的一個事件坛悉,但是又不是伐厌。由于Selector不是事件觸發(fā)的模式,而是主動去查詢的模式裸影,所以不叫事件Event挣轨,而是叫SelectionKey選擇鍵。

七轩猩、Selector的使用流程

7.1刃唐、創(chuàng)建Selector

Selector對象是通過調(diào)用靜態(tài)工廠方法open()來實例化的,如下:

// 1界轩、獲取Selector選擇器
Selector selector = Selector.open();

Selector的類方法open()內(nèi)部是向SPI發(fā)出請求画饥,通過默認(rèn)的SelectorProvider對象獲取一個新的實例。

7.2浊猾、將Channel注冊到Selector

要實現(xiàn)Selector管理Channel抖甘,需要將channel注冊到相應(yīng)的Selector上,如下:

// 2葫慎、獲取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// 3.設(shè)置為非阻塞
serverSocketChannel.configureBlocking(false);

// 4衔彻、綁定連接
serverSocketChannel.bind(new InetSocketAddress(SystemConfig.SOCKET_SERVER_PORT));

// 5、將通道注冊到選擇器上,并制定監(jiān)聽事件為:“接收”事件
serverSocketChannel.register(selector偷办,SelectionKey.OP_ACCEPT);

上面通過調(diào)用通道的register()方法會將它注冊到一個選擇器上艰额。

首先需要注意的是:

  • 與Selector一起使用時,Channel必須處于非阻塞模式下椒涯,否則將拋出異常IllegalBlockingModeException柄沮。這意味著,F(xiàn)ileChannel不能與Selector一起使用废岂,因為FileChannel不能切換到非阻塞模式祖搓,而套接字相關(guān)的所有的通道都可以。

另外湖苞,還需要注意的是:

  • 一個通道拯欧,并沒有一定要支持所有的四種操作。比如服務(wù)器通道ServerSocketChannel支持Accept 接受操作财骨,而SocketChannel客戶端通道則不支持镐作〔亟悖可以通過通道上的validOps()方法,來獲取特定通道下所有支持的操作集合该贾。

7.3羔杨、輪詢查詢就緒操作

通過Selector的select()方法,可以查詢出已經(jīng)就緒的通道操作靶庙,這些就緒的狀態(tài)集合问畅,包存在一個元素是SelectionKey對象的Set集合中。

下面是Selector幾個重載的查詢select()方法:

  • 1六荒、select():阻塞到至少有一個通道在你注冊的事件上就緒了护姆。
  • 2、select(long timeout):和select()一樣掏击,但最長阻塞事件為timeout毫秒卵皂。
  • 3、selectNow():非阻塞砚亭,只要有通道就緒就立刻返回灯变。

select()方法返回的int值,表示有多少通道已經(jīng)就緒捅膘,更準(zhǔn)確的說添祸,是自前一次select方法以來到這一次select方法之間的時間段上,有多少通道變成就緒狀態(tài)寻仗。

一旦調(diào)用select()方法刃泌,并且返回值不為0時,通過調(diào)用Selector的selectedKeys()方法來訪問已選擇鍵集合署尤,然后迭代集合的每一個選擇鍵元素耙替,根據(jù)就緒操作的類型,完成對應(yīng)的操作:

Set selectedKeys = selector.selectedKeys();

Iterator keyIterator = selectedKeys.iterator();

while(keyIterator.hasNext()) {

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()) {

        // a connection was accepted by a ServerSocketChannel.

    } else if (key.isConnectable()) {

        // a connection was established with a remote server.

    } else if (key.isReadable()) {

        // a channel is ready for reading

    } else if (key.isWritable()) {

        // a channel is ready for writing

    }

    keyIterator.remove();

}

處理完成后曹体,直接將選擇鍵俗扇,從這個集合中移除,防止下一次循環(huán)的時候箕别,被重復(fù)的處理铜幽。鍵可以但不能添加。試圖向已選擇的鍵的集合中添加元素將拋出java.lang.UnsupportedOperationException究孕。

八啥酱、一個NIO 編程的簡單實例

8.1、服務(wù)端:

/**
 * @Description: 服務(wù)端接收客戶端傳來的數(shù)據(jù)
 */
public class NIOServer {

    public static void main(String[] args) throws IOException {
        //創(chuàng)建ServerSocketChannel -> ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //得到一個Selecor對象
        Selector selector = Selector.open();

        //綁定一個端口6666, 在服務(wù)器端監(jiān)聽
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        //設(shè)置為非阻塞
        serverSocketChannel.configureBlocking(false);

        //把 serverSocketChannel 注冊到  selector 關(guān)心 事件為 OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 用輪詢的方式厨诸,查詢獲取“準(zhǔn)備就緒”的注冊過的操作
        while (true){
            //這里我們等待1秒,如果沒有事件發(fā)生, 返回
            if(selector.select(1000) == 0) { //沒有事件發(fā)生
                System.out.println("服務(wù)器等待了1秒禾酱,無連接");
                continue;
            }

            //如果返回的>0, 就獲取到相關(guān)的 selectionKey集合
            //1.如果返回的>0微酬, 表示已經(jīng)獲取到關(guān)注的事件
            //2. selector.selectedKeys() 返回關(guān)注事件的集合
            // 通過 selectionKeys 反向獲取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            //遍歷 Set<SelectionKey>, 使用迭代器遍歷
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while(iterator.hasNext()){
                //獲取到SelectionKey
                SelectionKey key = iterator.next();
                
                //處理key時绘趋,需要從selectionKeys集合中刪除,否則下次處理就會有問題
                iterator.remove();
                
                //根據(jù)key 對應(yīng)的通道發(fā)生的事件做相應(yīng)處理
                if(key.isAcceptable()) { //如果是 OP_ACCEPT, 有新的客戶端連接
                    //該該客戶端生成一個 SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println("客戶端連接成功 生成了一個 socketChannel " + socketChannel.hashCode());
                    //將  SocketChannel 設(shè)置為非阻塞
                    socketChannel.configureBlocking(false);
                    //將socketChannel 注冊到selector, 關(guān)注事件為 OP_READ颗管, 同時給socketChannel
                    //關(guān)聯(lián)一個Buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));

                    System.out.println("客戶端連接后 陷遮,注冊的selectionkey 數(shù)量=" + selector.keys().size()); //2,3,4..
                }
                if(key.isReadable()) {  //發(fā)生 OP_READ
                    try {
                        //通過key 反向獲取到對應(yīng)channel
                        SocketChannel channel = (SocketChannel)key.channel();

                        //獲取到該channel關(guān)聯(lián)的buffer
                        ByteBuffer buffer = (ByteBuffer)key.attachment();
                        //如果是正常斷開,read方法返回值是-1
                        int read = channel.read(buffer);
                        if(read == -1){
                            key.cancel();
                        }else {
                            System.out.println("form 客戶端 " + new String(buffer.array()));
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        //因為客戶端斷開了,因此需要將key取消(從selector的keys集合中真正刪除key)
                        key.cancel();
                    }
                }
            }
        }
    }
}

8.2垦江、客戶端:

public class NIOClient2 {

    public static void main(String[] args) throws IOException {
        //得到一個網(wǎng)絡(luò)通道
        SocketChannel socketChannel = SocketChannel.open();

        //設(shè)置非阻塞
        socketChannel.configureBlocking(false);
        //提供服務(wù)器端的ip 和 端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress("217.0.0.1", 6666);

        //連接服務(wù)器
        //socketChannel.connect(inetSocketAddress);
        //連接服務(wù)器
        if (!socketChannel.connect(inetSocketAddress)) {

            while (!socketChannel.finishConnect()) {
                System.out.println("因為連接需要時間帽馋,客戶端不會阻塞,可以做其它工作..");
            }
        }

        //...如果連接成功比吭,就發(fā)送數(shù)據(jù)
        String str = "hello, world";
        //Wraps a byte array into a buffer
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //發(fā)送數(shù)據(jù)绽族,將 buffer 數(shù)據(jù)寫入 channel
        socketChannel.write(buffer);
        //完畢時,清除緩沖區(qū)內(nèi)容
        buffer.clear();

        //關(guān)閉相關(guān)流
        socketChannel.close();
    }
}

九衩藤、SelectionKey

9.1吧慢、SelectionKey,表示 Selector 和網(wǎng)絡(luò)通道的注冊關(guān)系, 共四種:

  • int OP_ACCEPT:有新的網(wǎng)絡(luò)連接可以 accept赏表,值為 16
  • int OP_CONNECT:代表連接已經(jīng)建立检诗,值為 8
  • int OP_READ:代表讀操作,值為 1
  • int OP_WRITE:代表寫操作瓢剿,值為 4

源碼中:

public static final int OP_READ = 1 << 0; 
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

9.2逢慌、SelectionKey相關(guān)方法

public abstract class SelectionKey {

    public abstract Selector selector();//得到與之關(guān)聯(lián)的 Selector 對象
   
    public abstract SelectableChannel channel();//得到與之關(guān)聯(lián)的通道

    public final Object attachment();//得到與之關(guān)聯(lián)的共享數(shù)據(jù)

    public abstract SelectionKey interestOps(int ops);//設(shè)置或改變監(jiān)聽事件

    public final boolean isAcceptable();//是否可以 accept

    public final boolean isReadable();//是否可以讀

    public final boolean isWritable();//是否可以寫

}

十、ServerSocketChannel

10.1间狂、ServerSocketChannel 在服務(wù)器端監(jiān)聽新的客戶端 Socket 連接

10.2攻泼、相關(guān)方法如下

public abstract class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel{

    public static ServerSocketChannel open();得到一個 ServerSocketChannel 通道
    
    public final ServerSocketChannel bind(SocketAddress local);設(shè)置服務(wù)器端端口號
    
    public final SelectableChannel configureBlocking(boolean block);設(shè)置阻塞或非阻塞模式,取值 false 表示采用非阻塞模式
    
    public SocketChannel accept();接受一個連接前标,返回代表這個連接的通道對象
    
    public final SelectionKey register(Selector sel, int ops);注冊一個選擇器并設(shè)置監(jiān)聽事件
    
}

十一坠韩、SocketChannel

  • 10.1、SocketChannel炼列,網(wǎng)絡(luò) IO 通道只搁,具體負(fù)責(zé)進(jìn)行讀寫操作。NIO 把緩沖區(qū)的數(shù)據(jù)寫入通道俭尖,或者把通道里的數(shù)據(jù)讀到緩沖區(qū)氢惋。

  • 10.2、相關(guān)方法如下

public abstract class SocketChannel extends AbstractSelectableChannel 
    implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel{
    
    public static SocketChannel open();//得到一個 SocketChannel 通道
    
    public final SelectableChannel configureBlocking(boolean block);//設(shè)置阻塞或非阻塞模式稽犁,取值 false 表示采用非阻塞模式
    
    public boolean connect(SocketAddress remote);//連接服務(wù)器
    
    public boolean finishConnect();//如果上面的方法連接失敗焰望,接下來就要通過該方法完成連接操作
    
    public int write(ByteBuffer src);//往通道里寫數(shù)據(jù)
    
    public int read(ByteBuffer dst);//從通道里讀數(shù)據(jù)
    
    public final SelectionKey register(Selector sel, int ops, Object att);//注冊一個選擇器并設(shè)置監(jiān)聽事件,最后一個參數(shù)可以設(shè)置共享數(shù)據(jù)
    
    public final void close();//關(guān)閉通道
    
}

NIO編程小結(jié)

NIO編程的難度比同步阻塞BIO大很多已亥。

請注意以上的代碼中并沒有考慮“半包讀”和“半包寫”熊赖,如果加上這些,代碼將會更加復(fù)雜虑椎。

  • 1震鹉、客戶端發(fā)起的連接操作是異步的俱笛,可以通過在多路復(fù)用器注冊O(shè)P_CONNECT等待后續(xù)結(jié)果,不需要像之前的客戶端那樣被同步阻塞传趾。

  • 2迎膜、SocketChannel的讀寫操作都是異步的,如果沒有可讀寫的數(shù)據(jù)它不會同步等待浆兰,直接返回磕仅,這樣I/O通信線程就可以處理其他的鏈路,不需要同步等待這個鏈路可用簸呈。

  • 3榕订、線程模型的優(yōu)化:由于JDK的Selector在Linux等主流操作系統(tǒng)上通過epoll實現(xiàn),它沒有連接句柄數(shù)的限制(只受限于操作系統(tǒng)的最大句柄數(shù)或者對單個進(jìn)程的句柄限制)蝶棋,這意味著一個Selector線程可以同時處理成千上萬個客戶端連接卸亮,而且性能不會隨著客戶端的增加而線性下降。因此玩裙,它非常適合做高性能兼贸、高負(fù)載的網(wǎng)絡(luò)服務(wù)器。

參考:
https://www.cnblogs.com/crazymakercircle/p/9826906.html

https://www.cnblogs.com/CllOVER/p/13441282.html

https://www.cnblogs.com/snailclimb/p/9086334.html

https://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-selector.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吃溅,一起剝皮案震驚了整個濱河市溶诞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌决侈,老刑警劉巖螺垢,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赖歌,居然都是意外死亡枉圃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門庐冯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孽亲,“玉大人,你說我怎么就攤上這事展父》稻ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵栖茉,是天一觀的道長篮绿。 經(jīng)常有香客問我,道長吕漂,這世上最難降的妖魔是什么亲配? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上弃榨,老公的妹妹穿的比我還像新娘菩收。我一直安慰自己梨睁,他們只是感情好鲸睛,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坡贺,像睡著了一般官辈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遍坟,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天拳亿,我揣著相機(jī)與錄音,去河邊找鬼愿伴。 笑死肺魁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的隔节。 我是一名探鬼主播鹅经,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怎诫!你這毒婦竟也來了瘾晃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤幻妓,失蹤者是張志新(化名)和其女友劉穎蹦误,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肉津,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡强胰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了妹沙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偶洋。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖初烘,靈堂內(nèi)的尸體忽然破棺而出涡真,到底是詐尸還是另有隱情,我是刑警寧澤肾筐,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布哆料,位于F島的核電站,受9級特大地震影響吗铐,放射性物質(zhì)發(fā)生泄漏东亦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望典阵。 院中可真熱鬧奋渔,春花似錦、人聲如沸壮啊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歹啼。三九已至玄渗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狸眼,已是汗流浹背藤树。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拓萌,地道東北人岁钓。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像微王,于是被迫代替她去往敵國和親屡限。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361