教程
发布于: 2025年10月21日
作者: pbdecoder.online
什么是 CBOR?简洁二进制对象表示完整指南
CBOR(简洁二进制对象表示)完整指南 - 一种类似 Protocol Buffers 的二进制数据序列化格式,包含示例和对比分析
cbor
二进制格式
数据序列化
编码
json替代

什么是 CBOR?简洁二进制对象表示完整指南

概述

CBOR(Concise Binary Object Representation,简洁二进制对象表示)是在 RFC 7049 和 RFC 8949 中定义的二进制数据序列化格式。与 Protocol Buffers 类似,CBOR 设计为紧凑、快速,适用于受限环境。虽然 CBOR 数据本身是二进制的,人类无法直接阅读,但它有一种诊断表示法,可以将 CBOR 数据以人类可读的形式表示,用于调试和文档目的。

什么是 CBOR?

基本定义

CBOR 是一种以紧凑方式表示结构化数据的二进制编码格式。它的设计目标是:

  • 简洁:比 JSON 和 XML 更小
  • 快速:编码和解码速度快
  • 自描述:不需要模式定义
  • 可扩展:支持自定义数据类型
  • 互操作性:跨平台和语言工作
  • 二进制格式:人类无法直接阅读(除诊断表示法外)

核心特性

特性CBORJSONProtocol Buffers
需要模式

人类可读

否(二进制)

大小效率

非常高

解析速度

中等

非常快

数据类型丰富(23种类型)有限(6种类型)丰富(自定义)

CBOR 数据类型

CBOR 支持按主要类型组织的丰富数据类型集:

主要类型

// 主要类型 0:无符号整数(0-23, 24-255, 16位, 32位, 64位)
0, 1, 23, 24, 255, 65535, 4294967295

// 主要类型 1:负整数
-1, -24, -256, -65536

// 主要类型 2:字节字符串
h'48656c6c6f'  // 十六进制表示的 "Hello"

// 主要类型 3:文本字符串
"Hello, World!"

// 主要类型 4:数组
[1, 2, 3, "hello", true]

// 主要类型 5:映射(对象)
{"name": "John", "age": 30, "active": true}

// 主要类型 6:语义标签
1(1609459200)  // Unix 时间戳标签

// 主要类型 7:浮点数、简单值、中断
true, false, null, undefined, 3.14159

CBOR 诊断表示法

虽然 CBOR 数据以二进制格式存储且人类无法直接阅读,但 CBOR 规范定义了一种诊断表示法,提供 CBOR 数据的人类可读表示。这种表示法主要用于:

  • 文档编写:在规范中解释 CBOR 数据结构
  • 调试:在开发过程中理解 CBOR 消息的内容
  • 测试:编写具有可读 CBOR 数据表示的测试用例

诊断表示法示例

// 二进制 CBOR 数据(十六进制):0x83010203
// 诊断表示法:[1, 2, 3]

// 二进制 CBOR 数据(十六进制):0xA26161016162820203
// 诊断表示法:{"a": 1, "b": [2, 3]}

// 带语义标签的二进制 CBOR 数据
// 诊断表示法:1(1609459200)  // Unix 时间戳
// 诊断表示法:32("https://example.com")  // URI 标签

重要说明

诊断表示法不是实际的 CBOR 格式 - 它只是表示二进制 CBOR 数据内容的人类可读方式。在应用程序中使用 CBOR 时,您始终处理的是紧凑的二进制表示。

CBOR 与其他格式对比

大小对比示例

让我们比较不同格式中的相同数据:

// JSON(67 字节)
{
  "name": "Alice",
  "age": 25,
  "active": true,
  "scores": [95, 87, 92]
}
// CBOR(42 字节)- 诊断表示法(人类可读的表示形式)
{
  "name": "Alice",
  "age": 25,
  "active": true,
  "scores": [95, 87, 92]
}

// 实际的 CBOR 二进制数据(十六进制):
// A4646E616D65654C69636563616765186961637469766566F5667363...
// Protocol Buffers(约20字节,需要模式)
// 需要 .proto 定义文件

使用 CBOR

编码示例(JavaScript)

const cbor = require('cbor');

// 要编码的数据
const data = {
  name: "Alice",
  age: 25,
  active: true,
  scores: [95, 87, 92],
  timestamp: new Date()
};

// 编码为 CBOR
const encoded = cbor.encode(data);
console.log('CBOR 字节数:', encoded.length);
console.log('CBOR 十六进制:', encoded.toString('hex'));

// 从 CBOR 解码
const decoded = cbor.decode(encoded);
console.log('解码结果:', decoded);

编码示例(Python)

import cbor2
import datetime

# 要编码的数据
data = {
    'name': 'Alice',
    'age': 25,
    'active': True,
    'scores': [95, 87, 92],
    'timestamp': datetime.datetime.now()
}

# 编码为 CBOR
encoded = cbor2.dumps(data)
print(f'CBOR 字节数: {len(encoded)}')
print(f'CBOR 十六进制: {encoded.hex()}')

#  CBOR 解码
decoded = cbor2.loads(encoded)
print(f'解码结果: {decoded}')

流式处理示例

const cbor = require('cbor');
const fs = require('fs');

// 创建 CBOR 编码器流
const encoder = new cbor.Encoder();
const output = fs.createWriteStream('data.cbor');

encoder.pipe(output);

// 流式传输多个对象
encoder.write({id: 1, name: "Alice"});
encoder.write({id: 2, name: "Bob"});
encoder.write({id: 3, name: "Charlie"});
encoder.end();

// 使用解码器流读取
const decoder = new cbor.Decoder();
const input = fs.createReadStream('data.cbor');

input.pipe(decoder);

decoder.on('data', (obj) => {
  console.log('解码对象:', obj);
});

CBOR 二进制格式结构

基本结构

CBOR 使用简单的编码方案,每个数据项都以初始字节开始:

初始字节 = 主要类型(3位)+ 附加信息(5位)

位: 7 6 5 | 4 3 2 1 0
    ------+----------
    主要  | 附加
    类型  | 信息

编码示例

// 正整数 42
// 主要类型 0,附加信息 24(后跟1字节)
0x18, 0x2A

// 文本字符串 "CBOR"
// 主要类型 3,长度 4
0x64, 0x43, 0x42, 0x4F, 0x52

// 数组 [1, 2, 3]
// 主要类型 4,长度 3,然后是元素
0x83, 0x01, 0x02, 0x03

// 映射 {"a": 1}
// 主要类型 5,长度 1,然后是键值对
0xA1, 0x61, 0x61, 0x01

CBOR 高级特性

语义标签

CBOR 支持特殊数据类型的语义标签:

// 常见语义标签
const taggedData = {
  // 标签 0:标准日期/时间字符串
  datetime: cbor.Tagged(0, "2023-12-25T10:30:00Z"),
  
  // 标签 1:基于纪元的日期/时间
  timestamp: cbor.Tagged(1, 1703505000),
  
  // 标签 2:正大数
  bigint: cbor.Tagged(2, Buffer.from([0x01, 0x00, 0x00, 0x00, 0x00])),
  
  // 标签 21:期望 Base64url 编码
  base64url: cbor.Tagged(21, "SGVsbG8gV29ybGQ"),
  
  // 标签 32:URI
  uri: cbor.Tagged(32, "https://example.com")
};

不定长项目

CBOR 支持不定长数组和映射的流式处理:

// 不定长数组
const indefiniteArray = cbor.encode([
  cbor.BREAK,  // 不定长的特殊标记
  1, 2, 3, 4, 5
]);

// 不定长映射
const indefiniteMap = cbor.encode(new Map([
  [cbor.BREAK, null],  // 不定长标记
  ["key1", "value1"],
  ["key2", "value2"]
]));

使用场景和应用

物联网和受限设备

// 传感器数据传输
const sensorData = {
  deviceId: "sensor-001",
  temperature: 23.5,
  humidity: 65.2,
  battery: 87,
  timestamp: Date.now()
};

// CBOR 因其小尺寸而非常适合物联网
const cborData = cbor.encode(sensorData);
// 通过 LoRaWAN、NB-IoT 等传输

Web API

// Express.js 的 CBOR 中间件
app.use('/api/cbor', (req, res, next) => {
  if (req.headers['content-type'] === 'application/cbor') {
    let body = Buffer.alloc(0);
    req.on('data', chunk => {
      body = Buffer.concat([body, chunk]);
    });
    req.on('end', () => {
      req.body = cbor.decode(body);
      next();
    });
  } else {
    next();
  }
});

// API 端点
app.post('/api/cbor/data', (req, res) => {
  // 处理 CBOR 数据
  const result = processData(req.body);
  
  // 用 CBOR 响应
  res.setHeader('Content-Type', 'application/cbor');
  res.send(cbor.encode(result));
});

配置文件

// config.cbor - 二进制配置
const config = {
  server: {
    host: "localhost",
    port: 8080,
    ssl: true
  },
  database: {
    url: "mongodb://localhost:27017",
    options: {
      maxPoolSize: 10,
      serverSelectionTimeoutMS: 5000
    }
  },
  features: {
    authentication: true,
    logging: true,
    metrics: false
  }
};

// 保存为 CBOR
fs.writeFileSync('config.cbor', cbor.encode(config));

// 加载 CBOR 配置
const loadedConfig = cbor.decode(fs.readFileSync('config.cbor'));

性能考虑

编码性能

const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;

const testData = {
  users: Array.from({length: 1000}, (_, i) => ({
    id: i,
    name: `用户 ${i}`,
    email: `user${i}@example.com`,
    active: i % 2 === 0,
    scores: [Math.random() * 100, Math.random() * 100]
  }))
};

suite
  .add('JSON.stringify', () => {
    JSON.stringify(testData);
  })
  .add('CBOR.encode', () => {
    cbor.encode(testData);
  })
  .on('complete', function() {
    console.log('最快的是 ' + this.filter('fastest').map('name'));
  })
  .run();

内存使用

// 大数据集的内存高效流式处理
const stream = require('stream');

class CBORProcessor extends stream.Transform {
  constructor() {
    super({ objectMode: true });
  }
  
  _transform(chunk, encoding, callback) {
    try {
      // 处理每个 CBOR 对象
      const processed = this.processObject(chunk);
      this.push(cbor.encode(processed));
      callback();
    } catch (error) {
      callback(error);
    }
  }
  
  processObject(obj) {
    // 您的处理逻辑
    return obj;
  }
}

最佳实践

1. 选择合适的数据类型

// 好的做法:使用合适的数值类型
const data = {
  count: 42,           // 小整数
  price: 19.99,        // 浮点数
  id: BigInt(123456789012345)  // 大整数
};

// 避免:所有内容都用字符串
const badData = {
  count: "42",         // 应该是数字
  price: "19.99",      // 应该是数字
  id: "123456789012345"  // 可以是 BigInt
};

2. 使用语义标签

// 好的做法:为特殊类型使用语义标签
const eventData = {
  eventId: "evt-123",
  timestamp: cbor.Tagged(1, Math.floor(Date.now() / 1000)),
  location: cbor.Tagged(32, "https://maps.example.com/location/123"),
  metadata: cbor.Tagged(21, base64UrlEncode(metadataBuffer))
};

3. 优雅地处理错误

function safeCBORDecode(buffer) {
  try {
    return cbor.decode(buffer);
  } catch (error) {
    if (error.message.includes('Unexpected end of CBOR data')) {
      console.error('接收到不完整的 CBOR 数据');
      return null;
    }
    throw error;
  }
}

常见陷阱

1. 不定长度混淆

// 错误:混合定长和不定长
const wrongArray = [cbor.BREAK, 1, 2, 3];  // 不要这样做

// 正确:适当的不定长数组
const rightArray = cbor.encodeCanonical([1, 2, 3], {
  indefinite: true
});

2. 标签误用

// 错误:为数据类型使用错误的标签
const wrongDate = cbor.Tagged(2, "2023-12-25");  // 标签 2 用于大数

// 正确:日期的正确标签
const rightDate = cbor.Tagged(0, "2023-12-25T00:00:00Z");

总结

CBOR 是以下应用的绝佳选择:

  • 需要紧凑二进制编码而不需要模式要求的应用
  • 需要丰富数据类型支持超越 JSON 限制的应用
  • 在受限环境中需要快速解析的应用
  • 需要自描述格式进行灵活数据交换的应用
  • 需要大数据集流式处理能力的应用

虽然 Protocol Buffers 对于具有稳定模式的高性能应用可能更高效,但 CBOR 在效率、灵活性和易用性之间提供了很好的平衡,使其非常适合物联网、Web API 和配置文件。

延伸阅读

相关文章

什么是 Protocol Buffers 格式?完整解析
深入理解 Protocol Buffers 二进制格式的结构、原理和优势,掌握高效数据序列化技术
什么是 Protocol Buffers?完整介绍
全面了解 Google Protocol Buffers 的概念、优势、使用场景和核心特性
什么是 Protobuf 文件?完整指南
深入了解 Protobuf 文件的结构、语法和用途,从 .proto 文件到生成的代码