工作中遇到大小端数据存储格式
·
大小端数据存储
概念:
大端模式:是指数据的高字节保存在内存的低地址中,而低子节数据保存在内存的高地址中。
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
示例:32 位整数 0x12345678 在内存中从低地址到高地址:
| 字节序 | 地址递增方向的字节序列 |
|---|---|
| 大端 | 12 34 56 78 |
| 小端 | 78 56 34 12 |
在 iOS 上不能把 *(int*) 得到的 4 个字节直接当网络序用,必须用 htonl / 手动按大端拆字节。
Byte 类型(Objective-C / C)
Byte 是 UInt8 的别名,即无符号 8 位整数:
typedef UInt8 Byte; // UInt8 即 unsigned char
单字节字段应直接赋值,不要对长度为 1 的数组使用 *(int*) 写入(会越界,属于未定义行为)。
新的需求(业务协议):
这是移动端日志数据传给服务器的格式:
{
"flag": 16,
"item": {
"group": "xxxxxx",
"mirror": 1,
"items": [
{
"flag": 1,
"key": "log",
"datas": ["data:application/octet-stream;base64,xxxxxxx"]
}
]
}
}
datas 里是下面二进制包经 Base64 后的 Data URL。
内层二进制布局
[total_len(4B, 大端)]
[format(4B, ASCII,不足补 0x00)]
[type_len(1B)]
[type_padding_len(1B)]
[data_padding_len(1B)]
[reserved(1B)]
[type + type_padding] // type 原始 UTF-8,末尾补 0x00 至 4 字节对齐
[data + data_padding] // data 原始 UTF-8,末尾补 0x00 至 4 字节对齐
字段说明:
| 字段 | 说明 |
|---|---|
total_len | 整个二进制包长度(含 12 字节头),大端 |
format | 固定 4 字节,如 "json",不足 4 字节用 0x00 填充,超长则截断 |
type_len | type 的 UTF-8 字节长度(不含 padding) |
type_padding_len | type 对齐到 4 字节所需的 0x00 个数 |
data_padding_len | data 对齐到 4 字节所需的 0x00 个数 |
reserved | 保留,填 0x00 |
type / data | 业务字符串的 UTF-8 原始字节 |
对齐规则:padding_len = (4 - (len % 4)) % 4;需要 padding 时在末尾追加 0x00,不是 ASCII 字符 '0'(0x30)。
total_len 计算:
total_len = 12 + type_len + type_padding_len + data_len + data_padding_len
其中 data_len 为 JSON 字符串的 UTF-8 字节长度(不含 padding)。
疑惑:
之前不知道大小端数据是咋回事,一头雾水。问了安卓同事才勉强明白,我们先了解下Byte,Object-C里面其实是沿用C++的,下面是Byte的定义:
*********************************************************************************/
//Byte是UInt8的别名
typedef UInt8 Byte;
typedef SInt8 SignedByte;
typedef wide * WidePtr;
typedef UnsignedWide * UnsignedWidePtr;
typedef Float80 extended80;
typedef Float96 extended96;
typedef SInt8 VHSelect;
/*********************************************************************************
/********************************************************************************
Base integer types for all target OS's and CPU's
UInt8 8-bit unsigned integer
SInt8 8-bit signed integer
UInt16 16-bit unsigned integer
SInt16 16-bit signed integer
UInt32 32-bit unsigned integer
SInt32 32-bit signed integer
UInt64 64-bit unsigned integer
SInt64 64-bit signed integer
*********************************************************************************/
//UInt8是unsigned char 别名
typedef unsigned char UInt8;
typedef signed char SInt8;
typedef unsigned short UInt16;
typedef signed short SInt16;
#if __LP64__
typedef unsigned int UInt32;
typedef signed int SInt32;
#else
typedef unsigned long UInt32;
typedef signed long SInt32;
#endif
下面是Objective-C的实现:
工具方法
#import <arpa/inet.h>
/// 向 NSMutableData 追加 32 位无符号整数(大端 / 网络字节序)
static void AppendUInt32BigEndian(uint32_t value, NSMutableData *data) {
uint32_t be = htonl(value);
[data appendBytes:&be length:sizeof(be)];
}
/// 计算按 alignment 对齐所需的 padding 字节数
static NSUInteger PaddingLengthForAlignment(NSUInteger length, NSUInteger alignment) {
NSUInteger remainder = length % alignment;
return remainder == 0 ? 0 : (alignment - remainder);
}
/// UTF-8 数据 + 末尾 0x00 对齐;返回 padding 长度
static NSData *UTF8DataWithNullPadding(NSString *string,
NSUInteger alignment,
NSUInteger *outByteLength,
NSUInteger *outPaddingLength) {
NSData *raw = [string dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger byteLen = raw.length;
NSUInteger padLen = PaddingLengthForAlignment(byteLen, alignment);
if (outByteLength) {
*outByteLength = byteLen;
}
if (outPaddingLength) {
*outPaddingLength = padLen;
}
if (padLen == 0) {
return raw;
}
NSMutableData *padded = [raw mutableCopy];
uint8_t zero = 0;
for (NSUInteger i = 0; i < padLen; i++) {
[padded appendBytes:&zero length:1];
}
return padded;
}
/// format 固定 4 字节,不足补 0x00
static NSData *FormatFieldData(NSString *format) {
NSMutableData *field = [NSMutableData dataWithLength:4];
NSData *raw = [format dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger copyLen = MIN(raw.length, 4);
if (copyLen > 0) {
[field replaceBytesInRange:NSMakeRange(0, copyLen) withBytes:raw.bytes];
}
return field;
}
- (NSString *)logByteFill:(NSString *)data format:(NSString *)format type:(NSString *)type {
if (data.length == 0) {
return @"";
}
NSUInteger typeLen = 0;
NSUInteger typePaddingLen = 0;
NSData *typeData = UTF8DataWithNullPadding(type, 4, &typeLen, &typePaddingLen);
NSUInteger dataLen = 0;
NSUInteger dataPaddingLen = 0;
NSData *bodyData = UTF8DataWithNullPadding(data, 4, &dataLen, &dataPaddingLen);
NSData *formatData = FormatFieldData(format.length > 0 ? format : @"json");
uint32_t totalLen = (uint32_t)(12 + typeLen + typePaddingLen + dataLen + dataPaddingLen);
NSMutableData *packet = [NSMutableData dataWithCapacity:totalLen];
AppendUInt32BigEndian(totalLen, packet);
[packet appendData:formatData];
uint8_t headerTail[4];
headerTail[0] = (uint8_t)MIN(typeLen, UINT8_MAX);
headerTail[1] = (uint8_t)MIN(typePaddingLen, UINT8_MAX);
headerTail[2] = (uint8_t)MIN(dataPaddingLen, UINT8_MAX);
headerTail[3] = 0x00; // reserved
[packet appendBytes:headerTail length:4];
[packet appendData:typeData];
[packet appendData:bodyData];
NSString *baseString = [packet base64EncodedStringWithOptions:0];
return [NSString stringWithFormat:@"data:application/octet-stream;base64,%@", baseString];
}
测试:
//json 33 + cmd + 10 +补位json(3)+cmd(2)+固定长度12=60
NSString *json = @"{\"fileName\":\"log.ios.frfr4.h264\"}";
NSString *format = @"json";
NSString *cmd = @"openStream";
NSString *data = [self logByteFill:json format:format type:cmd];
NSLog(@"最后DATA == %@",data);
手工验算(上述示例):
type="openStream"→ 10 字节,padding 2 → 12 字节data(json)→ 33 字节,padding 3 → 36 字节total_len= 12 + 10 + 2 + 33 + 3 = 60(0x0000003C,大端:00 00 00 3C)format:6A 73 6F 6E("json")- 头后 4 字节:
0A 02 03 00(type_len, type_padding, data_padding, reserved)
可用十六进制查看 packet 与服务器解析结果比对。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)