Java NIO Socket編程實(shí)例

各I/O模型優(yōu)缺點(diǎn)
  • BIO通信模型

    BIO主要的問(wèn)題在于每當(dāng)有一個(gè)新的客戶端請(qǐng)求接入時(shí)赡模,服務(wù)端必須創(chuàng)建一個(gè)新的線程處理新接入的客戶端鏈路罢猪,一個(gè)線程只能處理一個(gè)客戶端連接

  • 線程池I/O編程

    假如所有可用線程都被阻塞,后續(xù)I/O都將在隊(duì)列中排隊(duì)
    線程池采用阻塞隊(duì)列實(shí)現(xiàn)醋界,隊(duì)列積滿之后竟宋,后續(xù)入隊(duì)列操作將被阻塞,新的客戶端請(qǐng)求被拒絕形纺,發(fā)生大量連接超時(shí)

  • NIO編程

    • 緩沖區(qū)Buffer

      每一種Java基本類型都有對(duì)一種緩沖區(qū)
      大多數(shù)標(biāo)準(zhǔn)I/O使用ByteBuffer

    • 通道Channel

      Channel分為兩大類:用于網(wǎng)絡(luò)讀寫(xiě)的SelectableChannel和用于文件操作的FileChannel

    • 多路復(fù)用器Selector

      多路復(fù)用器提供選擇已經(jīng)就緒的任務(wù)的能力

  • NIO2.0 AIO

    異步套接字通道不需要通過(guò)多路復(fù)用器(Selector)對(duì)注冊(cè)的通道進(jìn)行輪詢操作即可實(shí)現(xiàn)異步讀寫(xiě)

NIO實(shí)例分析
  • NIO服務(wù)端序列

    • 步驟一:打開(kāi)ServerSocketChannel丘侠,用于監(jiān)聽(tīng)客戶端的連接
    • 步驟二:綁定監(jiān)聽(tīng)端口,設(shè)置連接為非阻塞模式
    • 步驟三:創(chuàng)建Reactor線程逐样,創(chuàng)建多路復(fù)用器并啟動(dòng)線程
    • 步驟四:將ServerSocketChannel注冊(cè)到Reactor線程的多路復(fù)用器Selector上蜗字,監(jiān)聽(tīng)ACCEPT事件
    • 步驟五:多路復(fù)用器在線程run方法的無(wú)限循環(huán)體內(nèi)輪休準(zhǔn)備就緒的Key
    • 步驟六:多路復(fù)用器監(jiān)聽(tīng)到有新的客戶端接入,處理新的計(jì)入請(qǐng)求脂新,完成TCP三次握手挪捕,建立物理鏈路
    • 步驟七:設(shè)置客戶端鏈路為非阻塞模式
    • 步驟八:將新接入的客戶端連接注冊(cè)到Reactor線程的多路復(fù)用器上,監(jiān)聽(tīng)讀操作
    • 步驟九:異步讀取客戶端請(qǐng)求消息到緩沖區(qū)
    • 步驟十:對(duì)ByteBuffer進(jìn)行編解碼争便,如果有半包消息指針reset级零,繼續(xù)讀取后續(xù)的報(bào)文,將解碼成功的消息封裝成Task滞乙,投遞到業(yè)務(wù)線程池中
    • 步驟十一:將POJO對(duì)象encode成ByteBuffer奏纪,調(diào)用SocketChannel的異步write接口,將消息異步發(fā)送給客戶端
  • NIO客戶端序列

    • 步驟一:打開(kāi)SocketChannel斩启,綁定客戶端本機(jī)地址
    • 步驟二:設(shè)置SocketChannel為非阻塞模式序调,設(shè)置客戶端連接的TCP參數(shù)
    • 步驟三:異步連接服務(wù)器
    • 步驟四:判斷是否連接成功,如果連接成功兔簇,則直接注冊(cè)讀狀態(tài)位到多路復(fù)用器中发绢,如果當(dāng)前沒(méi)有連接成功
    • 步驟五:向Reactor線程的多路復(fù)用器注冊(cè)O(shè)P_CONNECT狀態(tài)位硬耍,監(jiān)聽(tīng)服務(wù)端的TCP ACK應(yīng)答
    • 步驟六:創(chuàng)建Reactor線程,創(chuàng)建多路復(fù)用器并啟動(dòng)線程
    • 步驟七:多路復(fù)用器在線程run方法的無(wú)限循環(huán)體內(nèi)輪詢準(zhǔn)備就緒的Key
    • 步驟八:接收connect事件進(jìn)行處理
    • 步驟九:判斷連接結(jié)果朴摊,如果連接成功默垄,注冊(cè)讀事件到多路復(fù)用器
    • 步驟十:注冊(cè)讀事件到多路復(fù)用器
    • 步驟十一:異步讀客戶端請(qǐng)求消息到緩沖區(qū)
    • 步驟十二:對(duì)ByteBuffer進(jìn)行編解碼,如果有半包消息接收緩沖區(qū)Reset甚纲,繼續(xù)讀取后續(xù)的報(bào)文口锭,將解碼成功的消息封裝成Task,投遞到業(yè)務(wù)線程池中介杆,進(jìn)行業(yè)務(wù)邏輯編排鹃操。
    • 步驟十三:將POJO對(duì)象encode成ByteBuffer,調(diào)用SocketChannel的異步write接口春哨,將消息異步發(fā)送給客戶端
NIO實(shí)例代碼
  • 服務(wù)端
    /**
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException{
        int port = 8080;
        if(args != null &&args.length >0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException ex){
                //采用默認(rèn)值
            }
        }
        MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);
        new Thread(timeServer,"NIO-MultiplexerTimeServer-001").start();
    }
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class MultiplexerTimeServer implements Runnable {

    private Selector selector;
    private ServerSocketChannel serverChannel;
    private volatile  boolean stop;

    /**
     * 初始化多路復(fù)用器荆隘,綁定監(jiān)聽(tīng)端口
     * @param port
     */
    public MultiplexerTimeServer(int port){
        try{
            selector = Selector.open();//創(chuàng)建多路復(fù)用器
            serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);//設(shè)置為異步非阻塞模式
            serverChannel.socket().bind(new InetSocketAddress(port),1024);//綁定端口
            serverChannel.register(selector,SelectionKey.OP_ACCEPT);//注冊(cè)到Selector
            System.out.println("The time server is start in port:" + port);
        }catch (IOException e){
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop(){
        this.stop = true;
    }

    public void run(){
        while(!stop){
            try{
               selector.select(1000);//selector每隔1s都被喚醒一次
               Set<SelectionKey> selectedKeys = selector.selectedKeys();
               Iterator<SelectionKey> it = selectedKeys.iterator();
               SelectionKey key = null;
               while(it.hasNext()){
                   key = it.next();
                   it.remove();
                   try{
                       handleInput(key);
                   }catch (Exception e){
                       if(key !=null){
                           key.cancel();
                           if(key.channel() !=null)
                               key.channel().close();
                       }
                   }
               }
            }catch (Throwable t){
                t.printStackTrace();
            }
        }
        //多路復(fù)用器關(guān)閉后,所有注冊(cè)在上面的Channel和Pipe等資源都會(huì)被自動(dòng)去注冊(cè)并關(guān)閉赴背,所以不需要重復(fù)釋放資源
        if(selector != null){
            try{
                selector.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    private void handleInput(SelectionKey key) throws IOException{
        if(key.isValid()){
            //處理新接入的請(qǐng)求消息
            if(key.isAcceptable()){
                //Accept the new connection
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                SocketChannel sc = ssc.accept();//接收客戶端的連接請(qǐng)求椰拒,完成TCP三次握手
                sc.configureBlocking(false);//設(shè)置為異步非阻塞
                //Add the new connection to the selector
                sc.register(selector,SelectionKey.OP_READ);
            }
            if(key.isReadable()){
                //Read the data
                SocketChannel sc = (SocketChannel)key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if(readBytes > 0 ){
                    readBuffer.flip();//將緩沖區(qū)當(dāng)前的limit設(shè)置為position,position設(shè)置為0
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes,"UTF-8");
                    System.out.println("The time server receive order :" + body);
                    String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?new java.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";
                    doWrite(sc,currentTime);
                }else if(readBytes <0){
                    //對(duì)端鏈路關(guān)閉
                    key.cancel();
                    sc.close();
                }else{
                    //讀到0字節(jié),忽略
                }
            }
        }
    }

    /**
     * 將應(yīng)答消息異步發(fā)送給客戶端
     * @param channel
     * @param response
     * @throws IOException
     */
    private void doWrite(SocketChannel channel,String response) throws IOException{
        if(response !=null && response.trim().length() >0){
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }
}
  • 客戶端
    /**
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException{
        int port = 8080;
        if(args != null &&args.length >0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException ex){
                //采用默認(rèn)值
            }
        }
        new Thread(new TimeClientHandle("127.0.0.1",port),"TimeClient-001").start();
    }
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class TimeClientHandle implements Runnable{
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean stop;

    public TimeClientHandle(String host,int port){
        this.host = host == null?"127.0.0.1":host;
        this.port = port;
        try{
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
        }catch (IOException e){
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void run(){
        try{
            doConnect();
        }catch (IOException e){
            e.printStackTrace();
            System.exit(1);
        }
        while(!stop){
            try{
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while(it.hasNext()){
                    key = it.next();
                    it.remove();
                    try{
                        handleInput(key);
                    }catch (Exception e){
                        if(key != null){
                            key.cancel();
                            if(key.channel() !=null)
                                key.channel().close();
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
                System.exit(1);
            }
        }
        if(selector !=null){
            try{
                selector.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    private void handleInput(SelectionKey key) throws IOException{
        if(key.isValid()){
            //判斷是否連接成功
            SocketChannel sc = (SocketChannel)key.channel();
            if(key.isConnectable()){
                if(sc.finishConnect()){
                    sc.register(selector,SelectionKey.OP_READ);
                    doWrite(sc);
                }else{
                    System.exit(1);//連接失敗凰荚,進(jìn)程退出
                }
            }
            if(key.isReadable()) {
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();//將緩沖區(qū)當(dāng)前的limit設(shè)置為position,position設(shè)置為0
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("The time server receive order :" + body);
                    this.stop = true;
                } else if (readBytes < 0) {
                    //對(duì)端鏈路關(guān)閉
                    key.cancel();
                    sc.close();
                } else {
                    //讀到0字節(jié)燃观,忽略
                }
            }
        }
    }

    private void doConnect() throws IOException{
        if(socketChannel.connect(new InetSocketAddress(host,port))){
            socketChannel.register(selector,SelectionKey.OP_READ);
            doWrite(socketChannel);
        }else{
            socketChannel.register(selector,SelectionKey.OP_CONNECT);
        }
    }

    private void doWrite(SocketChannel sc) throws IOException {
        byte[] bytes = "QUERY TIME ORDER".getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        writeBuffer.put(bytes);
        writeBuffer.flip();
        sc.write(writeBuffer);
        if (!writeBuffer.hasRemaining())
            System.out.println("Send order 2 server succeed.");
    }
}

先啟動(dòng)服務(wù)端,再啟動(dòng)客戶端運(yùn)行實(shí)例便瑟。

NIO2.0 AIO實(shí)例代碼
  • 服務(wù)端
    /**
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        int port = 8080;
        if(args != null &&args.length >0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException ex){
                //采用默認(rèn)值
            }
        }

        AsyncTimeServerHandler timeServer = new AsyncTimeServerHandler(port);
        new Thread(timeServer,"AIO-AsyncTimeServerHandler-001").start();
    }
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.CountDownLatch;

public class AsyncTimeServerHandler implements Runnable {
    private int port;
    CountDownLatch latch;
    AsynchronousServerSocketChannel asynchronousServerSocketChannel;

    public AsyncTimeServerHandler(int port){
        this.port = port;
        try{
            //創(chuàng)建一個(gè)異步的服務(wù)端通道
            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
            //綁定端口
            asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
            System.out.println("The time server is start in port:"+ port);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void run(){
        latch = new CountDownLatch(1);
        doAccept();
        try{
            latch.await();//允許當(dāng)前線程阻塞缆毁,防止服務(wù)端執(zhí)行完退出
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void doAccept(){
        //傳遞一個(gè)CompletionHandler實(shí)例來(lái)接收通知
        asynchronousServerSocketChannel.accept(this,new AcceptCompletionHandler());
    }
}

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler> {

    @Override
    public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) {
        //繼續(xù)接收
        attachment.asynchronousServerSocketChannel.accept(attachment, this);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        result.read(buffer, buffer, new ReadCompletionHandler(result));
    }

    @Override
    public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
        exc.printStackTrace();
        attachment.latch.countDown();
    }
}
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {

    private AsynchronousSocketChannel channel;

    public ReadCompletionHandler(AsynchronousSocketChannel channel){
        if(this.channel == null){
            this.channel = channel;
        }
    }

    @Override
    public void completed(Integer result,ByteBuffer attachment){
        attachment.flip();
        byte[] body = new byte[attachment.remaining()];
        attachment.get(body);
        try{
            String req = new String(body,"UTF-8");
            System.out.println("The time server receive order:"+req);
            String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(req)?
                    new java.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";
            doWrite(currentTime);
        }catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
    }

    private void doWrite(String currentTime){
        if(currentTime !=null && currentTime.trim().length()>0){
            byte[] bytes = (currentTime).getBytes();
            final ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer buffer) {
                    //如果沒(méi)有發(fā)送完成,繼續(xù)發(fā)送
                    if(buffer.hasRemaining())
                        channel.write(buffer,buffer,this);
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    try{
                        channel.close();
                    }catch (IOException e){
                        //ingnore on close
                    }
                }
            });
        }
    }

    public void failed(Throwable exc,ByteBuffer attachment){
        try{
            this.channel.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
  • 客戶端
    /**
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        int port = 8080;
        if(args != null &&args.length >0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException ex){
                //采用默認(rèn)值
            }
        }
        new Thread(new AsyncTimeClientHandler("127.0.0.1",port),"AIO-AsyncTimeClientHandler-001").start();
    }
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

public class AsyncTimeClientHandler implements CompletionHandler<Void,AsyncTimeClientHandler>,Runnable {
    private AsynchronousSocketChannel client;
    private String host;
    private int port;
    private CountDownLatch latch;

    public AsyncTimeClientHandler(String host,int port){
        this.host = host;
        this.port = port;
        try{
            client = AsynchronousSocketChannel.open();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public void run(){
        latch = new CountDownLatch(1);
        client.connect(new InetSocketAddress(host,port),this,this);
        try{
            latch.await();
        }catch (InterruptedException el){
            el.printStackTrace();
        }
        try{
            client.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public void completed(Void result,AsyncTimeClientHandler attachment){
        byte[] req = "QUERY TIME ORDER".getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
        writeBuffer.put(req);
        writeBuffer.flip();
        client.write(writeBuffer, writeBuffer,
                new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, final ByteBuffer buffer) {
                        if(buffer.hasRemaining()){
                            client.write(buffer,buffer,this);
                        }else{
                            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                            client.read(
                                    readBuffer,
                                    readBuffer,
                                    new CompletionHandler<Integer, ByteBuffer>() {
                                        @Override
                                        public void completed(Integer result, ByteBuffer attachment) {
                                            attachment.flip();
                                            byte[] bytes = new  byte[attachment.remaining()];
                                            attachment.get(bytes);
                                            String body;
                                            try{
                                                body = new String(bytes,"UTF-8");
                                                System.out.println("Now is:"+body);
                                                latch.countDown();
                                            }catch (UnsupportedEncodingException e){
                                                e.printStackTrace();
                                            }
                                        }

                                        @Override
                                        public void failed(Throwable exc, ByteBuffer attachment) {
                                            try{
                                                client.close();
                                                latch.countDown();
                                            }catch (IOException e){
                                                //ingnore on close
                                            }
                                        }
                                    }
                            );
                        }
                    }

                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        try{
                            client.close();
                            latch.countDown();
                        }catch (IOException e){
                            //ingnore on close
                        }
                    }
                });
    }

    @Override
    public void failed(Throwable exc,AsyncTimeClientHandler attachment){
        exc.printStackTrace();
        try{
            client.close();
            latch.countDown();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
GitHub地址

Java-DEMO/nettys/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末到涂,一起剝皮案震驚了整個(gè)濱河市脊框,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌践啄,老刑警劉巖浇雹,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異屿讽,居然都是意外死亡箫爷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)聂儒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虎锚,“玉大人,你說(shuō)我怎么就攤上這事衩婚〈芑ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵非春,是天一觀的道長(zhǎng)柱徙。 經(jīng)常有香客問(wèn)我缓屠,道長(zhǎng),這世上最難降的妖魔是什么护侮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任敌完,我火速辦了婚禮,結(jié)果婚禮上羊初,老公的妹妹穿的比我還像新娘滨溉。我一直安慰自己,他們只是感情好长赞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布晦攒。 她就那樣靜靜地躺著,像睡著了一般得哆。 火紅的嫁衣襯著肌膚如雪脯颜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天贩据,我揣著相機(jī)與錄音栋操,去河邊找鬼。 笑死饱亮,一個(gè)胖子當(dāng)著我的面吹牛讼庇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播近尚,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼场勤!你這毒婦竟也來(lái)了戈锻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤和媳,失蹤者是張志新(化名)和其女友劉穎格遭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體留瞳,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拒迅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了她倘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璧微。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖硬梁,靈堂內(nèi)的尸體忽然破棺而出前硫,到底是詐尸還是另有隱情,我是刑警寧澤荧止,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布屹电,位于F島的核電站阶剑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏危号。R本人自食惡果不足惜牧愁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望外莲。 院中可真熱鬧猪半,春花似錦、人聲如沸苍狰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淋昭。三九已至俐填,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翔忽,已是汗流浹背英融。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歇式,地道東北人驶悟。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像材失,于是被迫代替她去往敵國(guó)和親痕鳍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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