(十四)Dart WebSocket沧卢、Socket、IO醉者、字符編碼但狭、reflection

一、Sending and receiving real-time data with WebSocket(用WebSocket發(fā)送和接收實(shí)時(shí)數(shù)據(jù))

WebSocket類(lèi)在dart:io中撬即, 可以讓你的 web 應(yīng)用和服務(wù)器持續(xù)的交互數(shù)據(jù)立磁,不用 一直的輪詢。創(chuàng)建 WebSocket 的服務(wù)器會(huì)監(jiān)聽(tīng) ws:// 開(kāi)頭的 URL剥槐, 例如 ws://127.0.0.1:1337/ws唱歧。 通過(guò) WebSocket 發(fā)送的數(shù)據(jù)可以是字符串或者 blob。 通常都是使用 JSON 格式的字符串粒竖。

要在 web 應(yīng)用中使用 WebSocket颅崩,需要先創(chuàng)建 WebSocket 對(duì)象,把 WebSocket URL 作為該對(duì)象的參數(shù)蕊苗。

import 'dart:async';
import 'dart:io';

void webSocketDemo() async {
    // @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`'
    //      ' instead of `extends` if implementing this abstract class.')
    // var ws = new WebSocket('ws://127.0.0.1');

    var socket = await Socket.connect('ws://127.0.0.1', 4001);
    var ws = new WebSocket.fromUpgradedSocket(socket);
    // 不用的時(shí)候一定記得關(guān)閉
    // ws.close();
  }

1.1.Sending data(發(fā)送數(shù)據(jù))

使用 add() 函數(shù)向 WebSocket 發(fā)送數(shù)據(jù):

   /*
    static const int connecting = 0;
    static const int open = 1;
    static const int closing = 2;
    static const int closed = 3;
     */
    
    // int get readyState; 返回當(dāng)前連接狀態(tài)
    if(ws.readyState == WebSocket.open){ // 連接狀態(tài)
     /**
       * Sends data on the WebSocket connection. The data in [data] must
       * be either a `String`, or a `List<int>` holding bytes.
       */
   // 客戶端發(fā)送消息
    ws.add('Hello from Dart!'); 
 }

1.2.Receiving data(接收數(shù)據(jù))

要從 WebSocket 接收數(shù)據(jù)沿后,需要注冊(cè)一個(gè)事件 監(jiān)聽(tīng)器:

// 客戶端接收消息
ws.listen((onData){
  print('客戶端收到消息!');
  print(onData);
 });

1.3.Handling WebSocket events(處理WebSocket事件)

你的應(yīng)用可以處理如下的 WebSocket 事件:onError, onDone, cancelOnError, 和 (前面演示的) onData朽砰。下面是演示各種事件的示例:

    // 錯(cuò)誤處理
    ws.listen((onError){
      print(onError);
    });

    // 完成后處理
    ws.listen((onDone){
      print(onDone);
    });

    // 如果[cancelOnError]為:true尖滚,當(dāng)提交第一個(gè)錯(cuò)誤事件時(shí),訂閱將自動(dòng)取消
    // cancelOnError默認(rèn)值為:false.
    ws.listen((cancelOnError){
      print(cancelOnError);
    });

也可另一種方式監(jiān)聽(tīng)及處理事件:

ws.listen(dataHandler,
        onError: errorHandler,
        onDone: doneHandler,
        cancelOnError: false);

void dataHandler(data){
    print('received');
//    var a = String.fromCharCodes(value);
    var b = utf8.decode(data);
    print(b);
  }

  void errorHandler(error, StackTrace trace){
    print(error);
  }

  void doneHandler(){
    ...
  }

二、Socket

Socket和WebSocket類(lèi)似瞧柔,這里簡(jiǎn)單介紹一下好了漆弄。。造锅。

import 'dart:async';
import 'dart:io';
import 'dart:convert';


Socket socket;

  void socketDemo() async {

    var sock = await Socket.connect('ws://127.0.0.1', 4001).then((Socket s) {

      socket = s;
      socket.listen(dataHandler,
          onError: errorHandler,
          onDone: doneHandler,
          cancelOnError: false);
    }).catchError((AsyncError e) {
      print("Unable to connect: $e");
    });
    //Connect standard in to the socket
//  stdin.listen((data) => socket.write(new String.fromCharCodes(data).trim() + '\n'));
    // 發(fā)送消息
    socket.write('Hello Socket !\n');
  }

  void dataHandler(data){
    print('received');
//    var a = String.fromCharCodes(value);
    var b = utf8.decode(data);
    print(b);
  }

  void errorHandler(error, StackTrace trace){
    print(error);
  }

  void doneHandler(){

    /**
     * Destroy the socket in both directions. Calling [destroy] will make the
     * send a close event on the stream and will no longer react on data being
     * piped to it.
     *
     * Call [close](inherited from [IOSink]) to only close the [Socket]
     * for sending data.
     */
    socket.destroy();// 不用的時(shí)候一定記得調(diào)用
  }

除了上面提到的幾個(gè)功能外置逻,dart:io 庫(kù)還包含 processes, sockets,web sockets 等相關(guān)的 API。

三备绽、I/O for command-line apps(命令行應(yīng)用I/O)

dart:io 庫(kù) 提供了一些和 文件券坞、目錄、進(jìn)程肺素、sockets恨锚、 WebSockets、和 HTTP 客戶端以及服務(wù)器的 API倍靡。 只有命令行應(yīng)用可以使用 dart:io 庫(kù)猴伶,web app 無(wú)法使用。

一般而言,dart:io 庫(kù)實(shí)現(xiàn)和提供的是異步 API他挎。 同步函數(shù)很容易阻塞應(yīng)用筝尾,后期擴(kuò)展起來(lái)非常麻煩。 因此办桨,大部分的操作返回值都是 Future 或者 Stream 對(duì)象筹淫, 如果你熟悉 Node.js 則對(duì)這種模式會(huì)有所了解。

dart:io 里面也有一小部分同步方法呢撞,這些方法都使用 sync 前綴命名方法名字损姜。 這里就不再介紹這些同步方法了。

注意: 只有命令行應(yīng)用才能導(dǎo)入 dart:io殊霞。

3.1.Files and directories(文件和目錄)

I/O 庫(kù)可以讓命令行應(yīng)用讀寫(xiě)文件和查看目錄摧阅。 讀取文件有兩種方式:一次讀完或者通過(guò)流的方式來(lái)讀取。 一次讀完需要把文件內(nèi)容讀到內(nèi)存中绷蹲,如果文件 非常大或者你希望一邊讀文件一邊處理棒卷,則應(yīng)該 使用 Stream, 在 流式讀取文件中介紹祝钢。

3.1.1.Reading a file as text(以文本形式讀取文件)

對(duì)于編碼為 UTF-8 的文本比规,可以使用函數(shù) readAsString() 一次性 的讀取整個(gè)文本。如果單行文字比較重要太颤,則可以 使用 readAsLines() 來(lái)讀取苞俘。 這兩個(gè)函數(shù)返回一個(gè) Future 對(duì)象盹沈,當(dāng)文件 讀取完的時(shí)候龄章,可以從 Future 對(duì)象獲取一個(gè)或者多個(gè)字符串。

import 'dart:io';

main() async {
  var config = new File('config.txt');
  var contents;

  // Put the whole file in a single string.
  contents = await config.readAsString();
  print('The entire file is ${contents.length} characters long.');

  // Put each line of the file into its own string.
  contents = await config.readAsLines();
  print('The entire file is ${contents.length} lines long.');
}
3.1.2.Reading a file as binary(讀取二進(jìn)制文件)

下面的示例把文件數(shù)據(jù)讀取為字節(jié)流乞封。 同樣 readAsBytes() 函數(shù)返回值為 Future做裙, 當(dāng)讀完文件后,可以從 Future 中獲取數(shù)據(jù)肃晚。

import 'dart:io';

main() async {
  var config = new File('config.txt');

  var contents = await config.readAsBytes();
  print('The entire file is ${contents.length} bytes long');
}
3.1.3.Handling errors(錯(cuò)誤處理)

在 Future 上注冊(cè)一個(gè) catchError 來(lái)處理異常锚贱, 還可以在 async 方法中使用 try-catch 來(lái) 處理異常:

import 'dart:io';

main() async {
  var config = new File('config.txt');
  try {
    var contents = await config.readAsString();
    print(contents);
  } catch (e) {
    print(e);
  }
}
3.1.4.Streaming file contents(流媒體文件內(nèi)容)

使用 Stream 讀取文件的時(shí)候, 使用 Stream API 或者 await for 可以一點(diǎn)點(diǎn)的讀取关串, 詳情參考 異步支持拧廊。

import 'dart:io';
import 'dart:convert';
import 'dart:async';

main() async {
  var config = new File('config.txt');
  Stream<List<int>> inputStream = config.openRead();

  var lines = inputStream
      .transform(UTF8.decoder)
      .transform(new LineSplitter());
  try {
    await for (var line in lines) {
      print('Got ${line.length} characters from stream');
    }
    print('file is now closed');
  } catch (e) {
    print(e);
  }
}
3.1.5.Writing file contents(文件寫(xiě)內(nèi)容)
  • 使用 IOSink 可以往文件 寫(xiě)入內(nèi)容。使用 File 的 openWrite() 函數(shù)獲取到一個(gè) IOSink晋修。 默認(rèn)的寫(xiě)模式為 FileMode.WRITE吧碾,新寫(xiě)入的數(shù)據(jù)會(huì)完全覆蓋 文件之前的內(nèi)容。
var logFile = new File('log.txt');
var sink = logFile.openWrite();
sink.write('FILE ACCESSED ${new DateTime.now()}\n');
sink.close();
  • 如果想在文件末尾追加內(nèi)容墓卦,則可以使用 mode 可選參數(shù)倦春,參數(shù)取值 為 FileMode.APPEND
var sink = logFile.openWrite(mode: FileMode.APPEND);
  • 使用 add(List<int> data) 函數(shù)可以寫(xiě)二進(jìn)制數(shù)據(jù)到文件。
3.1.6.Listing files in a directory(列出目錄中的文件)

查找目錄中的所有文件和子目錄是一個(gè)異步操作。 list() 函數(shù)返回一個(gè) Stream睁本,當(dāng)遇到文件或者子目錄的時(shí)候尿庐, Stream 就發(fā)射一個(gè)對(duì)象。

import 'dart:io';

main() async {
  var dir = new Directory('/tmp');

  try {
    var dirList = dir.list();
    await for (FileSystemEntity f in dirList) {
      if (f is File) {
        print('Found file ${f.path}');
      } else if (f is Directory) {
        print('Found dir ${f.path}');
      }
    }
  } catch (e) {
    print(e.toString());
  }
}
3.1.7.Other common functionality(其他常見(jiàn)功能)

File 和 Directory 類(lèi)包含其他的一些文件操作呢堰, 下面只是一些常見(jiàn)的函數(shù):

  • 創(chuàng)建文件或者目錄: create() in File and Directory

  • 刪除文件或者目錄: delete() in File and Directory

  • 獲取文件的長(zhǎng)度: length() in File

  • 隨機(jī)位置訪問(wèn)文件: open() in File

參考 FileDirectory 的 API 文檔來(lái) 查看所有的函數(shù)抄瑟。

四、Decoding and encoding UTF-8 characters(解碼和編碼UTF-8字符)

  • 使用 utf8.decode() 來(lái)解碼 UTF8-encoded 字節(jié)流為 Dart 字符串:
import 'dart:convert' show utf8;

main() {
  var string = utf8.decode([
    0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
    0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
    0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
    0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
    0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1
  ]);
  print(string); // '???ér???????????????'
}
  • 如果是 stream 字節(jié)流則可以在 Stream 的 transform() 函數(shù)上指定 utf8.decoder
var lines = inputStream
    .transform(utf8.decoder)
    .transform(new LineSplitter());
try {
  await for (var line in lines) {
    print('Got ${line.length} characters from stream');
  }
  • 使用 utf8.encode() 把字符串編碼為 utf8 字節(jié)流:
import 'dart:convert' show utf8;

main() {
  List<int> expected = [
    0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
    0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
    0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
    0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
    0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1
  ];

  List<int> encoded = utf8.encode('???ér???????????????');

  assert(() {
    if (encoded.length != expected.length) return false;
    for (int i = 0; i < encoded.length; i++) {
      if (encoded[i] != expected[i]) return false;
    }
    return true;
  });
}

五暮胧、reflection(反射)

dart:mirrors 庫(kù)提供了基本的反射支持锐借。 使用 mirror 來(lái)查詢程序的結(jié)構(gòu),也可以 在運(yùn)行時(shí)動(dòng)態(tài)的調(diào)用方法或者函數(shù)往衷。

dart:mirrors 庫(kù)在命令行和 web 應(yīng)用中均可使用钞翔。 導(dǎo)入 dart:mirrors 即可開(kāi)始使用。

警告: 使用 dart:mirrors 可能會(huì)導(dǎo)致 dart2js 生成的 JavaScript 代碼 文件非常大席舍!
目前的解決方式是在導(dǎo)入 dart:mirrors 之前添加一個(gè) @MirrorsUsed 注解布轿。 詳情請(qǐng)參考 MirrorsUsedAPI 文檔。由于 dart:mirrors 庫(kù)依然還在 開(kāi)發(fā)中来颤,所以這個(gè)解決方案以后很有可能發(fā)生變化汰扭。

5.1.Symbols(符號(hào))

mirror 系統(tǒng)使用 Symbol 類(lèi)對(duì)象 來(lái)表達(dá)定義的 Dart 標(biāo)識(shí)符名字。 Symbols 在混淆后的代碼也可以 使用福铅。

如果在寫(xiě)代碼的時(shí)候萝毛,已經(jīng)知道 symbol 的名字了,則可以使用 #符號(hào)名字 的方式直接使用滑黔。 直接使用的 symbol 對(duì)象是編譯時(shí)常量笆包,多次定義引用的是同一個(gè)對(duì)象。 如果名字不知道略荡,則可以通過(guò) Symbol 構(gòu)造函數(shù)來(lái) 創(chuàng)建:

import 'dart:mirrors';

// If the symbol name is known at compile time.
const className = #MyClass;

// If the symbol name is dynamically determined.
var userInput = askUserForNameOfFunction();
var functionName = new Symbol(userInput);

在混淆代碼的時(shí)候庵佣,編譯器可能使用更加簡(jiǎn)短的名字來(lái)替代原來(lái)的符號(hào)(symbol)名字。 要獲取原來(lái)的 symbol 名字汛兜,使用 MirrorSystem.getName() 函數(shù)巴粪。該函數(shù) 在代碼混淆的情況下,也能返回正確的 symbol 名字粥谬。

import 'dart:mirrors';

const className = #MyClass;
assert('MyClass' == MirrorSystem.getName(className));

5.2.Introspection(檢查)

使用 mirror 功能來(lái)檢查程序的結(jié)構(gòu)肛根。可以檢查 類(lèi)漏策、庫(kù)以及對(duì)象等旨袒。

下面的示例使用 Person 類(lèi):

class Person {
  String firstName;
  String lastName;
  int age;

  Person(this.firstName, this.lastName, this.age);

  String get fullName => '$firstName $lastName';

  void greet(String other) {
    print('Hello there, $other!');
  }
}

在開(kāi)始使用之前洛勉,需要在一個(gè)類(lèi)或者對(duì)象上調(diào)用 reflect 函數(shù)來(lái)獲取 到 mirror丈秩。

5.2.1.Class mirrors(類(lèi)反射)

在任何 Type 上面調(diào)用 reflect 函數(shù)來(lái)獲取 ClassMirror :

ClassMirror mirror = reflectClass(Person);

assert('Person' ==
    MirrorSystem.getName(mirror.simpleName));

也可以在實(shí)例上調(diào)用 runtimeType 獲取該對(duì)象的 Type仅乓。

var person = new Person('Bob', 'Smith', 33);
ClassMirror mirror = reflectClass(person.runtimeType);
assert('Person' ==
    MirrorSystem.getName(mirror.simpleName));

獲取到 ClassMirror 后一也,就可以查詢類(lèi)的構(gòu)造函數(shù)、成員變量喉脖、等信息椰苟。 下面是列出類(lèi)的所有構(gòu)造函數(shù)的示例:

showConstructors(ClassMirror mirror) {
  var constructors = mirror.declarations.values
      .where((m) => m is MethodMirror && m.isConstructor);

  constructors.forEach((m) {
    print('The constructor ${m.simpleName} has '
          '${m.parameters.length} parameters.');
  });
}

下面是列出類(lèi)的成員變量的示例:

showFields(ClassMirror mirror) {
  var fields = mirror.declarations.values
      .where((m) => m is VariableMirror);

  fields.forEach((VariableMirror m) {
    var finalStatus = m.isFinal ? 'final' : 'not final';
    var privateStatus = m.isPrivate ?
        'private' : 'not private';
    var typeAnnotation = m.type.simpleName;

    print('The field ${m.simpleName} is $privateStatus ' +
          'and $finalStatus and is annotated as ' +
          '$typeAnnotation.');
  });
}

詳情請(qǐng)參考 ClassMirror 的 API 文檔

5.2.2.Instance mirrors (實(shí)例反射)

在對(duì)象上調(diào)用 reflect 函數(shù)可以獲取到一個(gè) InstanceMirror 對(duì)象树叽。

var p = new Person('Bob', 'Smith', 42);
InstanceMirror mirror = reflect(p);

如果你已經(jīng)有個(gè) InstanceMirror 對(duì)象了舆蝴,但是想知道該對(duì)象反射的目標(biāo)對(duì)象,則需要 調(diào)用 reflectee题诵。

var person = mirror.reflectee;
assert(identical(p, person));

5.3.Invocation(調(diào)度)

獲取到 InstanceMirror 后洁仗,就可以調(diào)用里面的函數(shù)、getter和setter了性锭。 詳細(xì)信息請(qǐng)參考 API docs for InstanceMirror 的 API 文檔赠潦。

5.3.1.Invoke methods(方法調(diào)度)

使用 InstanceMirror 的 invoke() 函數(shù)來(lái)調(diào)用對(duì)象的函數(shù)。 第一個(gè)參數(shù)為要調(diào)用的函數(shù)名字草冈,第二個(gè)參數(shù)為該函數(shù)的 一個(gè)位置參數(shù)列表她奥。第三個(gè)參數(shù)為可選參數(shù),用來(lái)指定命名 參數(shù)怎棱。

var p = new Person('Bob', 'Smith', 42);
InstanceMirror mirror = reflect(p);

mirror.invoke(#greet, ['Shailen']);
5.3.2.Invoke getters and setters(getter哩俭、setter調(diào)度)

使用 InstanceMirror 的 getField()setField() 函數(shù)來(lái) 查詢和設(shè)置對(duì)象的屬性。

var p = new Person('Bob', 'Smith', 42);
InstanceMirror mirror = reflect(p);

// Get the value of a property.
var fullName = mirror.getField(#fullName).reflectee;
assert(fullName == 'Bob Smith');

// Set the value of a property.
mirror.setField(#firstName, 'Mary');
assert(p.firstName == 'Mary');

文章 Reflection in Dart with Mirrors 更詳細(xì) 的介紹了反射功能拳恋。另外也可以參考 dart:mirror, 特別是 MirrorsUsed, ClassMirror,InstanceMirror 這些類(lèi)的 API 文檔凡资。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谬运,隨后出現(xiàn)的幾起案子隙赁,更是在濱河造成了極大的恐慌,老刑警劉巖吩谦,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸳谜,死亡現(xiàn)場(chǎng)離奇詭異膝藕,居然都是意外死亡式廷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)芭挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)滑废,“玉大人,你說(shuō)我怎么就攤上這事袜爪∪涑茫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵辛馆,是天一觀的道長(zhǎng)俺陋。 經(jīng)常有香客問(wèn)我豁延,道長(zhǎng),這世上最難降的妖魔是什么腊状? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任诱咏,我火速辦了婚禮,結(jié)果婚禮上缴挖,老公的妹妹穿的比我還像新娘袋狞。我一直安慰自己,他們只是感情好映屋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布苟鸯。 她就那樣靜靜地躺著,像睡著了一般棚点。 火紅的嫁衣襯著肌膚如雪早处。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天瘫析,我揣著相機(jī)與錄音陕赃,去河邊找鬼。 笑死颁股,一個(gè)胖子當(dāng)著我的面吹牛么库,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播甘有,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼诉儒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了亏掀?” 一聲冷哼從身側(cè)響起忱反,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滤愕,沒(méi)想到半個(gè)月后温算,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡间影,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年注竿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魂贬。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巩割,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出付燥,到底是詐尸還是另有隱情宣谈,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布键科,位于F島的核電站闻丑,受9級(jí)特大地震影響漩怎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗦嗡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一扬卷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酸钦,春花似錦怪得、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至欢伏,卻和暖如春入挣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背硝拧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工径筏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人障陶。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓滋恬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抱究。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恢氯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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