ProtocolBuffer在Swift中的应用
一、ProtocolBuffer简介
ProtocolBuffer(也称PB/GPB): google 的一种数据交换的格式, 可以实现跨平台, 方便的序列化&反序列化
-
跨平台
ProtoBuf支持多平台和语言, 包括C++/Java/Python/Swift等等 -
序列化&反序列号
ProtoBuf支持直接将对象序列化成Data, 也支持直接将Data序列化为对象类型 -
消息大小
一条消息数据,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二进制序列化的10分之一
对于网络编程来说, 减小数据量的大小非常有必须
二、安装
安装过程可以参考:https://github.com/alexeyxo/protobuf-swift
- 环境安装
在mac终端中
brew install protobuf-swift
如果没有安装brew可以先执行以下指令
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- 客户端集成(通过cocoapods)
use_frameworks!
pod 'ProtocolBuffers-Swift'
三、ProtocolBuffer在Swift中的应用
-
创建.proto文件
在项目中, 创建一个(或多个).proto文件
之后会通过该文件, 自动帮我们生成需要的源文件(比如C++生成.cpp源文件, 比如java生成.java源文件, Swift就生成.swift源文件) -
源码规范
syntax = "proto2";
message Person {
required int64 id = 1;
required string name = 2;
optional string email = 3;
}
- 具体说明
1.syntax = "proto2"; 为定义使用的版本号, 目前常用版本proto2/proto3
2.message是消息定义的关键字,等同于C++/Swift中的struct/class,或是Java中的class
3.Person为消息的名字,等同于结构体名或类名
4.required前缀表示该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值
5.optional前缀表示该字段为可选字段, 既在序列化和反序列化时可以没有被赋值
6.repeated通常被用在数组字段中
7.int64和string分别表示整型和字符串型的消息字段
8.id和name和email分别表示消息字段名,等同于Swift或是C++中的成员变量名
9.标签数字1和2则表示不同的字段在序列化后的二进制数据中的布局位置, 需要注意的是该值在同一message中不能重复
- 定义有枚举类型Protocol Buffer消息
enum UserStatus {
OFFLINE = 0; //表示处于离线状态的用户
ONLINE = 1; //表示处于在线状态的用户
}
message UserInfo {
required int64 acctID = 1;
required string name = 2;
required UserStatus status = 3;
}
- 定义有类型嵌套
enum UserStatus {
OFFLINE = 0;
ONLINE = 1;
}
message UserInfo {
required int64 acctID = 1;
required string name = 2;
required UserStatus status = 3;
}
message LogonRespMessage {
required LoginResult logonResult = 1;
required UserInfo userInfo = 2;
}
- 代码编写完成后, 生成对应语言代码
protoc xxx.proto --swift_out="./"
四、案例:直播中的聊天室
IMMessage.proto
syntax = "proto2";
message UserInfo {
required string name = 1;
required int64 level = 2;
}
message TextMessage {
required UserInfo user = 1;
required string text = 2;
}
message GiftMessage {
required UserInfo user = 1;
required string giftname = 2;
required string giftURL = 3;
required string giftCount = 4;
}
进入IMMessage.proto所在的路径,执行
protoc IMMessage.proto --swift_out="./"
生成Immessage.proto.swift
然后在应用中,这样使用
func sendJoinRoom() {
// 1.获取消息的长度
let msgData = (try! userInfo.build()).data()
// 2.发送消息
sendMsg(data: msgData, type: 0)
}
func sendLeaveRoom() {
// 1.获取消息的长度
let msgData = (try! userInfo.build()).data()
// 2.发送消息
sendMsg(data: msgData, type: 1)
}
func sendTextMsg(message : String) {
// 1.创建TextMessage类型
let chatMsg = ChatMessage.Builder()
chatMsg.text = message
// 2.获取对应的data
let chatData = (try! chatMsg.build()).data()
// 3.发送消息到服务器
sendMsg(data: chatData, type: 2)
}
func sendGiftMsg(giftName : String, giftURL : String, giftCount : Int) {
// 1.创建GiftMessage
let giftMsg = GiftMessage.Builder()
giftMsg.giftname = giftName
giftMsg.giftUrl = giftURL
giftMsg.giftcount = Int32(giftCount)
// 2.获取对应的data
let giftData = (try! giftMsg.build()).data()
// 3.发送礼物消息
sendMsg(data: giftData, type: 3)
}
func sendMsg(data : Data, type : Int) {
// 1.将消息长度, 写入到data
var length = data.count
let headerData = Data(bytes: &length, count: 4)
// 2.消息类型
var tempType = type
let typeData = Data(bytes: &tempType, count: 2)
// 3.发送消息
let totalData = headerData + typeData + data
tcpClient.send(data: totalData)
}