简单记录下如何在 Dart 中使用 Protobuf。
参考
注意点:
安装和编译 第一步,安装 Protobuf 编译器。可以从 Protobuf release 页面下载和安装,也可以 brew install protobuf 安装。
第二步,安装 Protobuf Dart 插件。
下载代码 。git clone https://github.com/dart-lang/protobuf.git
编译插件 。调用 pub install 编译插件,编译后源码 bin 目录下可以找到 proto-gen-dart 文件。如果出错,可以使用 pub --trace install 查看详细错误日志
使用插件。将插件配置到 PATH 路径中,或者调用 protoc 时使用 --plugin 参数指定插件路径
第三步,运行 protoc 编译生成 .proto.dart 文件。
1 2 3 4 5 6 ➜ aproj_pub_proj git:(cm) ✗ protoc --proto_path=proto --dart_out=build/gen --plugin=/Users/xxx/Documents/GitHub/protobuf/protoc_plugin/bin proto/aproj/comm_conn.proto protoc-gen-dart: program not found or is not executable --dart_out: protoc-gen-dart: Plugin failed with status code 1. ➜ aproj_pub_proj git:(cm) ✗ protoc --proto_path=proto --dart_out=build/gen --plugin=/Users/xxx/Documents/GitHub/protobuf/protoc_plugin/bin/protoc-gen-dart proto/aproj/comm_conn.proto /Users/kingcmchen/Documents/GitHub/protobuf/protoc_plugin/bin/protoc-gen-dart: line 3: dart: command not found --dart_out: protoc-gen-dart: Plugin failed with status code 127.
第一次出错是因为 --plugin 指定的 Dart 插件路径不正确,应当指定具体文件而不是文件所在的目录
第二次出错是因为 Dart 插件依赖 dart 命令,要确保 PATH 中有配置 dart。
PATH 中配置 dart 命令方法如下:
1 export DART_PATH="$HOME/flutter/bin/cache/dart-sdk/bin"
编译成功!
发送字符串 先使用 Dart 实现简单的服务器端 SimpleServer 和客户端 SimpleClient,代码分别如下。
SimpleServer 收到客户端发送的数据,转换成大写的 UTF-8 后发回客户端,并关闭 Socket。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import 'dart:convert' ;import 'dart:io' ;main() async { ServerSocket serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 6760 ); print ('Started' ); serverSocket.listen((Socket socket) { socket.listen((List <int > event) async { var msg = utf8.decode(event); print ('Received $msg ' ); socket.write(msg.toUpperCase()); await socket.close(); }); }); }
SimpleClient 向服务器端发送 ‘hello’,并且接收和输出服务器端的响应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import 'dart:async' ;import 'dart:convert' ;import 'dart:io' ;main() async { Socket socket = await Socket.connect('127.0.0.1' , 6760 ); print ('Connected' ); socket.listen((List <int > event) { print (utf8.decode(event)); }); socket.add(utf8.encode('hello' )); await Future.delayed(Duration (seconds: 5 )); socket.close(); }
发送 Protobuf 数据 如何在使用 Protobuf 数据在 SimpleServer 和 SimpleClient 之间通信?
注意,生成的 .pb.dart 文件中有如下 import:
1 2 import 'package:fixnum/fixnum.dart'; import 'package:protobuf/protobuf.dart' as $pb;
所以相应地需要在 pubspec.yaml 中添加对应的依赖。
第一步,为工程添加 fixnum 和 protobuf 依赖。添加后记得运行 flutter pb get 同步一下。
1 2 3 4 5 6 dependencies: ... http: 0.12.0+2 fixnum: 0.10.9 protobuf: 0.13.15
第二步,将生成的 .pb.dart 文件拷贝到工程,供 SimpleServer 和 SimpleClient 引用。
第三步,修改服务器端 SimpleServer 和客户端 SimpleClient 代码,使用 Protobuf 通信。修改后的代码分别如下:
SimpleServer 收到客户端发送的 Protobuf 数据并以 JSON 格式打印出来,然后向客户端发送 Protobuf 数据 CommRsp,最后关闭 Socket。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import 'dart:io' ;import 'comm_conn.pb.dart' ;main() async { ServerSocket serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 6760 ); print ('Started' ); serverSocket.listen((Socket socket) { socket.listen((List <int > event) async { var msg = CommReq.fromBuffer(event).writeToJson(); print ('Received $msg ' ); var rsp = CommRsp.create(); rsp.cmd = 0 ; rsp.result = 0 ; rsp.uid = '41006' ; socket.add(rsp.writeToBuffer()); socket.close(); }); }); }
SimpleClient 向服务器端发送 Protobuf 数据 CommReq,并且接收和打印服务器端的响应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import 'dart:io' ;import 'comm_conn.pb.dart' ;main() async { Socket socket = await Socket.connect('127.0.0.1' , 6760 ); print ('Connected' ); socket.listen((List <int > event) { print (CommRsp.fromBuffer(event)); }); var req = CommReq.create(); req.cmd = 0 ; req.uid = 'cm' ; req.ext1 = 'hello' ; socket.add(req.writeToBuffer()); await socket.close(); }
注意以下两点:
注意 Protobuf 数据字段分为 required 字段和 optional 字段
使用 socket.add() 发送数据而不是 socket.write()。注意 socket.write() 先对要发送的数据编码后再调用 socket.add(),实际发送的是编码后的数据
1 2 3 4 5 void write(Object obj) { String string = '$obj ' ; if (string.isEmpty) return ; add(_encoding.encode(string)); }
如何在 Dart 中创建和解析 Protobuf 数据,请参考官方文档 。