在日常学习、工作或生活中,大家总少不了接触作文或者范文吧,通过文章可以把我们那些零零散散的思想,聚集在一块。写范文的时候需要注意什么呢?有哪些格式需要注意呢?下面我给大家整理了一些优秀范文,希望能够帮助到大家,我们一起来看一看吧。
不同坐标系之间的转换方法篇一
引导语:网络传输中,数据包与数据流的相互转换都有哪些方法呢?以下是小编整理的包与流之间的转换方法,欢迎参考阅读!
这种办法粗暴简单,我们使用一个特殊字符来作为包与包之间的分隔符,不过这个分隔符要特殊,特殊到几乎不出现在包的内容当中,否则会影响接收方切割包的过程。
作为发送方,我们可以用如下代码(示意用):
#define kseparatorchar @"¤"
+ (nsstring*)encodetextpayload:(nsstring*)payload {
nsstring* str = [nsstring stringwithformat:@"%@%@", kseparatorchar, payload];
return str;
}
¤ 就是一个非常特殊的字符,一般应用层的文本都不会涉及到,所以可以用作我们的特殊分隔符。接收端只需要以 ¤ 为分隔符,再把数据做一次切割即可:
+ (nsstring*)decodetextpayloadstring:(nsstring*)str {
nsstring* payload;
nsarray* arr = [str componentsseparatedbystring:kseparatorchar];
if ( < 2) {
return nil;
}
payload = arr[1];
return payload;
}
这种做法的缺陷也是显而易见的,必须严格要求包体中不会出现该特殊字符,所以这种办法只能应用于非常特殊的场景。
这种办法也是粗暴简单,甚至不需要分隔符,每次接收方从 stream 中取出固定长度的字节,还原成一个包,代码也比较简单,在 receive() 回调里,每次检查是否达到了固定的长度,是则取出固定长度还原,否则继续等待,代码就不演示啦。
这种做法的缺陷就更大了,会造成包体的浪费,无法适应不同大小的包。
之前一篇介绍自定义通讯协议的文章里,简单的提到过如何设计一个可用的协议,这里我们具体看下代码。
当我们需要描述可变长度的`包时,需要定义一个 header 来详细描述包相关的信息,比如最简单的,记录包的长度。如何记录包的大小呢?我们可以用位操作的特性,来将应用层的 int 值放入到包的 header 中,代码如下(代码摘自以前的项目,稍有改动):
- (nsdata*)encodedata:(nsdata*)data withheader:(nsstring*)header {
int datasize = (int);
char buffer[4];
buffer[0] = datasize >> 24;
buffer[1] = (datasize << 8) >> 24;
buffer[2] = (datasize << 16) >> 24;
buffer[3] = (datasize << 24) >> 24;
nsmutabledata* packet = [nsmutabledata new];
[packet appendbytes:[header utf8string] length:2];
[packet appendbytes:buffer length:4];
[packet appenddata:data];
return packet;
}
这是一个通用的技巧,当我们需要在 stream 中记录可变长度的数据时,都可以用这种位操作来做转换,只需要 2 个字节的长度,即可记录长达 64 kb 的数据长度,4 个字节则能记录长达 4 gb 的长度。
接收方在收到 nsdata 之后,可以先读取 4 个字节的长度信息,还原成 int 值,再读取 int 值所记录的字节数,这些字节就是我们的包了,代码如下:
- (tdecodeddata*)decodedata:(nsdata*)data {
tdecodeddata* d = [tdecodeddata new];
if ( < 6) { //minimal packet length
return nil;
}
if ([headerstr isequaltostring:kpacketstreamheader] == true) {
int realsize = 0;
unsigned char buffer[4];
[data getbytes:buffer range:nsmakerange(2, 4)];
realsize += buffer[0] << 24;
realsize += buffer[1] << 16;
realsize += buffer[2] << 8;
realsize += buffer[3] << 0;
if ( - 6 < realsize) {
return nil;
}
= kpacketstreamheader;
nsdata* payloadbytes = [data subdatawithrange:nsmakerange(6, realsize)];
if ( > 0) {
ddata = payloadbytes;
}
//remove from data
int handledlength = 6 + realsize;
nsdata* nd = [nsdata datawithbytes: + handledlength length:-handledlength];
ddata = nd;
}
return d;
}
上面的代码主要是向大家展示,如何以添加 header 的方式,来记录可变长度的包体信息。如此,发送方所发送的 nsdata 就和接收方所接受的 nsdata 一一对应起来了,就就不存在所谓的粘包和拆包问题了。
我们之所以可以对一个 stream 做切分,是因为 tcp 已经做了可靠传输的保证,接收方收到的 stream 和发送方发送的 stream 严格一致,一个字节都不会差,所以我们只需要先读取长度值,再按长度值读取后续的数据,就能把一个 stream 分割成一个个的 nsdata,这些分割好的 nsdata 就是发送方所发送的包了。
接收方将 stream 分割成 nsdata 之后,需要进一步将 data 反序列化成应用层的包,这里就必须提到 google 开源的 protobuf 了,序列化和反序列化神器,造福了无数的框架和应用,甚至有 objective c 的版本。
s("content_relate");【包与流之间的转换方法】相关文章:
java进制之间的转换
10-01
word文件的转换方法
10-06
php ascii码与字符串相互转换的方法
09-11
c语言中网络地址与二进制数之间转换
11-20
java中float类型的范围及其与十六进制的转换方法
11-28
php字母大小写转换的方法
08-21
java输入数据流的方法有哪些
12-07
java中的stream流的解析与应用
12-01
c++中时间与时间戳的转换
10-04
word表格中数据纵横转换的方法和技巧
09-21