iOS项目中使用protobuf
上一篇讲了protobuf环境的配置(iOS 使用protobuf(一)环境的配置),这一篇将会讲到protobuf在项目中的实际的使用。
因为protobuf最后会被编译成字节流,通过socket发送个服务端,所以就牵涉到以下几个问题:
1)将数据发送到服务端的时候,服务端怎么样去将接收到的数据从数据包中解析出来的问题?
2)怎么样去判断接收到的数据是完整的?
3)怎么样截取完整的数据,并且映射成对象?
4)怎么样同时发起多个请求
一:通信我们现在的做法是依赖socket来发送和接收
使用socket就会牵扯到粘包的问题:
1)为了判断是否该包数据是否是完整的一包数据,我们的包头是包体内容经过base128编码后的长度的值,来作为包头。
-(NSMutableData *)dataWithRawVarint32:(int64_t)value//进行128编码
{
NSMutableData *valData = [[NSMutableData alloc] init];
while (true) {
if ((value & ~0x7F) == 0) {//如果最高位是0,只要一个字节表示
[valData appendBytes:&value length:1];
break;
} else {
int valChar = (value & 0x7F) | 0x80;//先写入低7位,最高位置1
[valData appendBytes:&valChar length:1];
value = value >> 7;//再写高7位
}
}
return valData;
}
2)服务器收到数据之后就会先切出包头的数据,获取到包体的数据,判读一下长度是否是一样的。如果不一样那么这包数据就是作废的。直接告诉客户端是失败的,如果是完整的数据,处理完,返回正确的数据给客户端
二:解析返回来的数据,判断完整性
1)将返回来的数据进行base128反编码,获取到包体的长度,用这个包长度截取出包体的数据
int length =(int)[self getProtoBUfLenght:data];
newData= [data subdataWithRange:NSMakeRange(data.length-length, length)];
-(NSInteger)getProtoBUfLenght:(NSData *)data//反编码 获取到真是的数据体的长度 {
int32_t lenth=0;
GPBCodedInputStream * input = [[GPBCodedInputStream alloc] initWithData:data];
enth = (int32_t)[input readInt64];
return lenth;
}
2)然后去判断length和包头中的数据长度是否一致
三:依赖(二)中获取到的newData,将newData进行protobuf编码,映射成对象。然后我们就能直接像使用oc对象一样去使用这个对象
QPack * pack = [QPack parseFromData:newData error:nil];
进行第三步的时候有可能会解析失败,原因有可能是服务端返回来的数据格式不是标准的protobuf数据格式,所有就会解析失败,这时候只需要将data直接转换为字符串,打印比较一下就好
四:同时发起多个请求
为什么要说这个,那是因为有可能网络因素和服务端响应等影响,有可能就会出现数据混乱的问题。
比如说:我在a页面发起了a的请求,过了一会没有响应。现在来到b页面发起了b请求,但是现在返回来的数据确实a请求的数据。这样就会发生数据错乱的问题。怎么解决呢?
当然解决的办法很多,我的做法是,在protobuffer中增加一个messageid(使用当前的时间经过base128编码生成来的)的字段。每次都是用这个字段去判断返回来的消息,就不会出现错乱的问题,也可以同时进行多请求。