android藍(lán)牙BLE(二) —— 通信

一、藍(lán)牙基礎(chǔ)協(xié)議

想了解藍(lán)牙通信之前县踢,需要先了解藍(lán)牙兩個(gè)最基本的協(xié)議:GAP 和 GATT

GAP(Generic Access Profile)簡(jiǎn)介

? GAP是通用訪問配置文件的首字母縮寫疏之,主要控制藍(lán)牙連接和廣播殿雪。GAP使藍(lán)牙設(shè)備對(duì)外界可見,并決定設(shè)備是否可以或者怎樣與其他設(shè)備進(jìn)行交互锋爪。

GAP定義了多種角色丙曙,但主要的兩個(gè)是:中心設(shè)備 和 外圍設(shè)備。

  • 中心設(shè)備:可以掃描并連接多個(gè)外圍設(shè)備,從外設(shè)中獲取信息其骄。

  • 外圍設(shè)備:小型亏镰,低功耗,資源有限的設(shè)備拯爽∷髯ィ可以連接到功能更強(qiáng)大的中心設(shè)備,并為其提供數(shù)據(jù)毯炮。

GAP廣播數(shù)據(jù)

GAP 中外圍設(shè)備通過兩種方式向外廣播數(shù)據(jù):廣播數(shù)據(jù) 和 掃描回復(fù)( 每種數(shù)據(jù)最長(zhǎng)可以包含 31 byte逼肯。)。

? 廣播數(shù)據(jù)是必需的桃煎,因?yàn)橥庠O(shè)必需不停的向外廣播篮幢,讓中心設(shè)備知道它的存在。而掃描回復(fù)是可選的为迈,中心設(shè)備可以向外設(shè)請(qǐng)求掃描回復(fù)三椿,這里包含一些設(shè)備額外的信息。

image

外圍設(shè)備會(huì)設(shè)定一個(gè)廣播間隔葫辐。每個(gè)廣播間隔中搜锰,它會(huì)重新發(fā)送自己的廣播數(shù)據(jù)。廣播間隔越長(zhǎng)耿战,越省電蛋叼,同時(shí)也不太容易掃描到。

廣播的網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)

? 外設(shè)通過廣播自己讓中心設(shè)備發(fā)現(xiàn)自己剂陡,并建立 GATT 連接鸦列,從而進(jìn)行更多的數(shù)據(jù)交換租冠。但有些情況是不需要連接的,只要外設(shè)廣播自己的數(shù)據(jù)即可薯嗤。目的是讓外圍設(shè)備顽爹,把自己的信息發(fā)送給多個(gè)中心設(shè)備。因?yàn)榛?GATT 連接的方式的骆姐,只能是一個(gè)外設(shè)連接一個(gè)中心設(shè)備镜粤。


image

GATT(Generic Attribute Profile)簡(jiǎn)介

? GATT配置文件是一個(gè)通用規(guī)范,用于在BLE鏈路上發(fā)送和接收被稱為“屬性”的數(shù)據(jù)塊玻褪。目前所有的BLE應(yīng)用都基于GATT肉渴。

BLE設(shè)備通過叫做 ServiceCharacteristic 的東西進(jìn)行通信

? GATT使用了 ATT(Attribute Protocol)協(xié)議,ATT 協(xié)議把 Service, Characteristic對(duì)應(yīng)的數(shù)據(jù)保存在一個(gè)查詢表中带射,次查找表使用 16 bit ID 作為每一項(xiàng)的索引同规。

? GATT 連接是獨(dú)占的。也就是一個(gè) BLE 外設(shè)同時(shí)只能被一個(gè)中心設(shè)備連接窟社。一旦外設(shè)被連接券勺,它就會(huì)馬上停止廣播,這樣它就對(duì)其他設(shè)備不可見了灿里。當(dāng)外設(shè)與中心設(shè)備斷開关炼,外設(shè)又開始廣播,讓其他中心設(shè)備感知該外設(shè)的存在匣吊。而中心設(shè)備可同時(shí)與多個(gè)外設(shè)進(jìn)行連接儒拂。

image

GATT 通信

中心設(shè)備和外設(shè)需要雙向通信的話,唯一的方式就是建立 GATT 連接色鸳。

? GATT 通信的雙方是 C/S 關(guān)系社痛。外設(shè)作為 GATT 服務(wù)端(Server),它維持了 ATT 的查找表以及 service 和 characteristic 的定義命雀。中心設(shè)備是 GATT 客戶端(Client)蒜哀,它向 外設(shè)(Server) 發(fā)起請(qǐng)求來獲取數(shù)據(jù)。

GATT 結(jié)構(gòu)

image
  • Profile:并不是實(shí)際存在于 BLE 外設(shè)上的咏雌,它只是一個(gè)被 Bluetooth SIG 或者外設(shè)設(shè)計(jì)者預(yù)先定義的 Service 的集合。例如心率ProfileHeart Rate Profile)就是結(jié)合了 Heart Rate ServiceDevice Information Service校焦。

  • Service:包含一個(gè)或者多個(gè) Characteristic赊抖。每個(gè) Service 有一個(gè) UUID 唯一標(biāo)識(shí)。

  • Characteristic: 是最小的邏輯數(shù)據(jù)單元寨典。一個(gè)Characteristic包括一個(gè)單一value變量和0-n個(gè)用來描述characteristic變量的Descriptor氛雪。與 Service 類似,每個(gè) Characteristic 用 16 bit 或者 128 bit 的 UUID 唯一標(biāo)識(shí)耸成。

實(shí)際開發(fā)中报亩,和 BLE 外設(shè)打交道浴鸿,主要是通過 Characteristic∠易罚可以從 Characteristic 讀取數(shù)據(jù)岳链,也可以往 Characteristic 寫數(shù)據(jù),從而實(shí)現(xiàn)雙向的通信劲件。

    UUID 有 16 bit 掸哑、32bit 和 128 bit 的。16 bit 的 UUID 是官方通過認(rèn)證的零远,需要花錢購(gòu)買苗分。

Bluetooth_Base_UUID定義為 00000000-0000-1000-8000-00805F9B34FB

  • 若16 bit UUID為xxxx,轉(zhuǎn)換為128 bit UUID為0000xxxx-0000-1000-8000-00805F9B34FB
  • 若32 bit UUID為xxxxxxxx牵辣,轉(zhuǎn)換為128 bit UUID為xxxxxxxx-0000-1000-8000-00805F9B34FB

二摔癣、中心設(shè)備與外設(shè)通訊

簡(jiǎn)單介紹BLE開發(fā)當(dāng)中各種主要類和其作用:

  • BluetoothDeivce:藍(lán)牙設(shè)備,代表一個(gè)具體的藍(lán)牙外設(shè)纬向。
  • BluetoothGatt:通用屬性協(xié)議择浊,定義了BLE通訊的基本規(guī)則和操作
  • BluetoothGattCallback:GATT通信回調(diào)類,用于回調(diào)的各種狀態(tài)和結(jié)果罢猪。
  • BluetoothGattService:服務(wù)近她,由零或多個(gè)特征組構(gòu)成。
  • BluetoothGattCharacteristic:特征膳帕,里面包含了一組或多組數(shù)據(jù)粘捎,是GATT通信中的最小數(shù)據(jù)單元。
    BluetoothGattDescriptor:特征描述符危彩,對(duì)特征的額外描述攒磨,包括但不僅限于特征的單位,屬性等汤徽。

獲取藍(lán)牙設(shè)備對(duì)象

對(duì)掃描到的藍(lán)牙可以用集合形式進(jìn)行緩存娩缰,也可只保存其mac地址,存儲(chǔ)到字符集合中谒府,用于后續(xù)的連接拼坎。

根據(jù)mac地址獲取到BluetoothDeivce用于連接

BluetoothManager bluetoothmanager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
 mBluetoothAdapter = bluetoothmanager.getAdapter();
 //獲取藍(lán)牙設(shè)備對(duì)象進(jìn)行連接
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(macAddressStr)

藍(lán)牙gatt回調(diào)

實(shí)現(xiàn)BluetoothGattCallBack類,監(jiān)聽藍(lán)牙連接過程中各種回調(diào)的監(jiān)聽完疫。藍(lán)牙Gatt回調(diào)方法中都不應(yīng)該進(jìn)行耗時(shí)操作泰鸡,需要將其方法內(nèi)進(jìn)行的操作丟進(jìn)另一個(gè)線程,盡快返回壳鹤。

//定義子線程handle盛龄,用于在BluetoothGattCallback中回調(diào)方法中的操作拋到該線程工作。
private Handler mHandler;
//定義handler工作的子線程
private HandlerThread mHandlerThread;

初始化handler
mHandlerThread = new HandlerThread("daqi");
mHandlerThread.start();
//將handler綁定到子線程中
mHandler = new Handler(mHandlerThread.getLooper());


//定義藍(lán)牙Gatt回調(diào)類
public class daqiBluetoothGattCallback extends BluetoothGattCallback{
        //連接狀態(tài)回調(diào)
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            // status 用于返回操作是否成功,會(huì)返回異常碼。
            // newState 返回連接狀態(tài)余舶,如BluetoothProfile#STATE_DISCONNECTED啊鸭、BluetoothProfile#STATE_CONNECTED
            
            //操作成功的情況下
            if (status == BluetoothGatt.GATT_SUCCESS){
                //判斷是否連接碼
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                
                }else if(newState == BluetoothProfile.STATE_DISCONNECTED){
                    //判斷是否斷開連接碼
                    
                }
            }else{
                //異常碼
                
            }
        }
        
        //服務(wù)發(fā)現(xiàn)回調(diào)
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
        }

        //特征寫入回調(diào)
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }
        
        //外設(shè)特征值改變回調(diào)
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
        }
        
        //描述寫入回調(diào)
        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
        }
    }

連接設(shè)備

? 調(diào)用BluetoothDevice#connectGatt()進(jìn)行ble連接,第二個(gè)參數(shù)默認(rèn)選擇false,不自動(dòng)連接匿值。并定義BluetoothGatt變量赠制,存儲(chǔ)BluetoothDevice#connectGatt()返回的對(duì)象。

image

//定義Gatt實(shí)現(xiàn)類
private BluetoothGatt mBluetoothGatt; 

//創(chuàng)建Gatt回調(diào)
private BluetoothGattCallback mGattCallback = new daqiBluetoothGattCallback();
//連接設(shè)備
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    mBluetoothGatt = mBluetoothDevice.connectGatt(mContext,
            false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
    mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback);
}
連接異常處理

? 藍(lán)牙連接時(shí)千扔,不一定百分百連接成功憎妙。連接出錯(cuò)時(shí),會(huì)返回異常碼進(jìn)行錯(cuò)誤描述曲楚。對(duì)于大多數(shù)異常碼厘唾,可以通過重連來達(dá)到連接成功的目的。

錯(cuò)誤代碼:

  • 133 :連接超時(shí)或未找到設(shè)備龙誊。
  • 8 : 設(shè)備超出范圍
  • 22 :表示本地設(shè)備終止了連接
//定義重連次數(shù)
private int reConnectionNum = 0;
//最多重連次數(shù)
private int maxConnectionNum = 3;

public class daqiBluetoothGattCallback extends BluetoothGattCallback{

    //連接狀態(tài)回調(diào)
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        // status 用于返回操作是否成功,會(huì)返回異常碼抚垃。
        //操作成功的情況下
        if (status == BluetoothGatt.GATT_SUCCESS){
            
        }else{
            //重連次數(shù)不大于最大重連次數(shù)
            if(reConnectionNum < maxConnectionNum){
                //重連次數(shù)自增
                reConnectionNum++
                //連接設(shè)備
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    mBluetoothGatt = mBluetoothDevice.connectGatt(mContext,
                            false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
                } else {
                    mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback);
                }
            }else{
                //斷開連接,返回連接失敗回調(diào)
                
            }
        }
    }
    
    //其他回調(diào)方法
}

發(fā)現(xiàn)服務(wù)

連接成功后趟大,觸發(fā)BluetoothGattCallback#onConnectionStateChange()方法鹤树。

public class daqiBluetoothGattCallback extends BluetoothGattCallback{

    //連接狀態(tài)回調(diào)
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        // status 用于返回操作是否成功,會(huì)返回異常碼。
        //操作成功的情況下
        if (status == BluetoothGatt.GATT_SUCCESS){
            //判斷是否連接碼
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    //可延遲發(fā)現(xiàn)服務(wù)逊朽,也可不延遲
                    mHandler.post(() ->
                        //發(fā)現(xiàn)服務(wù)
                        mBluetoothGatt.discoverServices();
                    );
                }else if(newState == BluetoothProfile.STATE_DISCONNECTED){
                    //判斷是否斷開連接碼
                    
                }
        }
    }
    
    //其他回調(diào)方法
}

當(dāng)發(fā)現(xiàn)服務(wù)成功后罕伯,會(huì)觸發(fā)BluetoothGattCallback#onServicesDiscovered()回調(diào):

//定義需要進(jìn)行通信的ServiceUUID
private UUID mServiceUUID = UUID.fromString("0000xxxx-0000-1000-8000-00805f9b34fb");

//定義藍(lán)牙Gatt回調(diào)類
public class daqiBluetoothGattCallback extends BluetoothGattCallback{

    //服務(wù)發(fā)現(xiàn)回調(diào)
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mHandler.post(() ->
                //獲取指定uuid的service
                BluetoothGattService gattService = mBluetoothGatt.getService(mServiceUUID);
                //獲取到特定的服務(wù)不為空
                if(gattService != null){
                    
                }else{
                    //獲取特定服務(wù)失敗
                    
                }
            );
        }
    }
}
發(fā)現(xiàn)服務(wù)失敗

? 發(fā)現(xiàn)服務(wù)時(shí),會(huì)存在發(fā)現(xiàn)不了特定服務(wù)的情況叽讳∽匪或者說,整個(gè)BluetoothGatt對(duì)象中的服務(wù)列表為空岛蚤。
BluetoothGatt類中存在一個(gè)隱藏的方法refresh()邑狸,用于刷新Gatt的服務(wù)列表。當(dāng)發(fā)現(xiàn)不了服務(wù)時(shí)涤妒,可以通過反射去調(diào)用該方法单雾,再發(fā)現(xiàn)一遍服務(wù)。

讀取和修改特征值

//定義需要進(jìn)行通信的ServiceUUID
private UUID mServiceUUID = UUID.fromString("0000xxxx-0000-1000-8000-00805f9b34fb");
//定義需要進(jìn)行通信的CharacteristicUUID
private UUID mCharacteristicUUID = UUID.fromString("0000yyyy-0000-1000-8000-00805f9b34fb");


//定義藍(lán)牙Gatt回調(diào)類
public class daqiBluetoothGattCallback extends BluetoothGattCallback{

    //服務(wù)發(fā)現(xiàn)回調(diào)
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mHandler.post(() ->
                //獲取指定uuid的service
                BluetoothGattService gattService = mBluetoothGatt.getService(mServiceUUID);
                //獲取到特定的服務(wù)不為空
                if(gattService != null){
                    //獲取指定uuid的Characteristic
                    BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(mCharacteristicUUID);
                    //獲取特定特征成功
                    if(gattCharacteristic != null){
                        //寫入你需要傳遞給外設(shè)的特征值(即傳遞給外設(shè)的信息)
                        gattCharacteristic.setValue(bytes);
                        //通過GATt實(shí)體類將她紫,特征值寫入到外設(shè)中硅堆。
                        mBluetoothGatt.writeCharacteristic(gattCharacteristic);
                        
                        //如果只是需要讀取外設(shè)的特征值:
                        //通過Gatt對(duì)象讀取特定特征(Characteristic)的特征值
                        mBluetoothGatt.readCharacteristic(gattCharacteristic);
                    }
                }else{
                    //獲取特定服務(wù)失敗
                    
                }
            );
        }
    }
}

當(dāng)成功讀取特征值時(shí),會(huì)觸發(fā)BluetoothGattCallback#onCharacteristicRead()回調(diào)贿讹。

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicRead(gatt, characteristic, status);
    if (status == BluetoothGatt.GATT_SUCCESS) {
        //獲取讀取到的特征值
        characteristic.getValue()
    }
}

當(dāng)成功寫入特征值到外設(shè)時(shí)渐逃,會(huì)觸發(fā)BluetoothGattCallback#onCharacteristicWrite()回調(diào)。

@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    if (status == BluetoothGatt.GATT_SUCCESS) {
        //獲取寫入到外設(shè)的特征值
        characteristic.getValue()
    }
}

監(jiān)聽外設(shè)特征值改變

? 無論是對(duì)外設(shè)寫入新值围详,還是讀取外設(shè)特定Characteristic的值朴乖,其實(shí)都只是單方通信。如果需要雙向通信助赞,可以在BluetoothGattCallback#onServicesDiscovered中對(duì)某個(gè)特征值設(shè)置監(jiān)聽(前提是該Characteristic具有NOTIFY屬性):

//設(shè)置訂閱notificationGattCharacteristic值改變的通知
mBluetoothGatt.setCharacteristicNotification(notificationGattCharacteristic, true);
//獲取其對(duì)應(yīng)的通知Descriptor
BluetoothGattDescriptor descriptor = notificationGattCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (descriptor != null){ 
    //設(shè)置通知值
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
    boolean descriptorResult = mBluetoothGatt.writeDescriptor(descriptor);
}

? 當(dāng)寫入完特征值后买羞,外設(shè)修改自己的特征值進(jìn)行回復(fù)時(shí),手機(jī)端會(huì)觸發(fā)BluetoothGattCallback#onCharacteristicChanged()方法雹食,獲取到外設(shè)回復(fù)的值畜普,從而實(shí)現(xiàn)雙向通信。

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
     if (status == BluetoothGatt.GATT_SUCCESS) {
        //獲取外設(shè)修改的特征值
        String value = characteristic.getValue()
        //對(duì)特征值進(jìn)行解析
        
    }
}

斷開連接

斷開連接的操作分為兩步:

  • mBluetoothGatt.disconnect();
  • mBluetoothGatt.close();

? 調(diào)用disconnect()后群叶,會(huì)觸發(fā)手機(jī)會(huì)觸發(fā)BluetoothGattCallback#onConnectionStateChange()的回調(diào)吃挑,回調(diào)斷開連接信息,newState = BluetoothProfile.STATE_DISCONNECTED街立。但調(diào)用完disconnect()緊接著馬上調(diào)用close()舶衬,會(huì)終止BluetoothGattCallback#onConnectionStateChange()的回調(diào)∈昀耄可以看情況將兩個(gè)進(jìn)行拆分調(diào)用逛犹,來實(shí)現(xiàn)斷開連接,但必須兩個(gè)方法都調(diào)用梁剔。

例如:
需要在外設(shè)修改特征值觸發(fā)BluetoothGattCallback#onCharacteristicChanged()時(shí)虽画,斷開連接∪俨。可以先在BluetoothGattCallback#onCharacteristicChanged()中調(diào)用disconnect(),并等調(diào)用BluetoothGattCallback#onConnectionStateChange()回調(diào)码撰,返回?cái)嚅_連接信息后,再調(diào)用close()對(duì)Gatt資源進(jìn)行關(guān)閉个盆。

當(dāng)和外設(shè)進(jìn)行ble通信時(shí)脖岛,如出現(xiàn)任何意外情況,馬上調(diào)用斷開連接操作砾省。

android BLE系列:

android藍(lán)牙BLE(一) —— 掃描

android藍(lán)牙BLE(二) —— 通信

android藍(lán)牙BLE(三) —— 廣播

android藍(lán)牙BLE(四) —— 實(shí)戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鸡岗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子编兄,更是在濱河造成了極大的恐慌轩性,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狠鸳,死亡現(xiàn)場(chǎng)離奇詭異揣苏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)件舵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門卸察,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铅祸,你說我怎么就攤上這事坑质『衔洌” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵涡扼,是天一觀的道長(zhǎng)稼跳。 經(jīng)常有香客問我,道長(zhǎng)吃沪,這世上最難降的妖魔是什么汤善? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮票彪,結(jié)果婚禮上红淡,老公的妹妹穿的比我還像新娘。我一直安慰自己降铸,他們只是感情好在旱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著推掸,像睡著了一般颈渊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上终佛,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天俊嗽,我揣著相機(jī)與錄音,去河邊找鬼铃彰。 笑死绍豁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的牙捉。 我是一名探鬼主播竹揍,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼邪铲!你這毒婦竟也來了芬位?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤带到,失蹤者是張志新(化名)和其女友劉穎昧碉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揽惹,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡被饿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搪搏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狭握。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖疯溺,靈堂內(nèi)的尸體忽然破棺而出论颅,到底是詐尸還是另有隱情哎垦,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布恃疯,位于F島的核電站撼泛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏澡谭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一损俭、第九天 我趴在偏房一處隱蔽的房頂上張望蛙奖。 院中可真熱鬧,春花似錦杆兵、人聲如沸雁仲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攒砖。三九已至,卻和暖如春日裙,著一層夾襖步出監(jiān)牢的瞬間吹艇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工昂拂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留受神,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓格侯,卻偏偏與公主長(zhǎng)得像鼻听,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子联四,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345