一、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
參考 File 和 Directory 的 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 文檔凡资。