网络数据大端序和小端序

实际应用场景

1. 网络协议解析

swift 复制代码
// 假设接收到的消息格式:
// [4字节nonce长度][nonce数据][实际载荷数据]
let message: Data = ... // 接收到的完整消息

// 读取nonce长度
let nonceLength = message[..<4].withUnsafeBytes {
    Int($0.loadUnaligned(as: Int32.self).byteSwapped)
}

// 提取nonce数据
let nonceRange = 4..<(4 + nonceLength)
let nonceData = message[nonceRange]

// 剩余的是实际载荷
let payloadRange = (4 + nonceLength)..
let payloadData = message[payloadRange]

字节序的重要性

为什么需要 .byteSwapped

swift 复制代码
// 假设网络传输的数据是大端序:0x00 0x00 0x01 0x00 (表示256)
let networkData = Data([0x00, 0x00, 0x01, 0x00])

// 不加 byteSwapped(错误):
let wrongLength = networkData.withUnsafeBytes { 
    Int($0.loadUnaligned(as: Int32.self)) // 得到 65536 ❌
}

// 加 byteSwapped(正确):
let correctLength = networkData.withUnsafeBytes { 
    Int($0.loadUnaligned(as: Int32.self).byteSwapped) // 得到 256 ✅
}

安全考虑

loadUnaligned vs load

swift 复制代码
// 使用 loadUnaligned 更安全,因为:
message[..<4].withUnsafeBytes {
    // 这4个字节可能没有按照Int32的内存对齐要求
    Int($0.loadUnaligned(as: Int32.self).byteSwapped) // ✅ 安全
}

// 如果用 load,在某些架构上可能崩溃
message[..<4].withUnsafeBytes {
    Int($0.load(as: Int32.self).byteSwapped) // ❌ 可能崩溃
}

完整的工作流程

swift 复制代码
func parseEncryptedMessage(_ message: Data) -> (nonce: Data, payload: Data)? {
    guard message.count >= 4 else { return nil }
    
    // 1. 读取nonce长度
    let nonceLength = message[..<4].withUnsafeBytes {
        Int($0.loadUnaligned(as: Int32.self).byteSwapped)
    }
    
    // 2. 验证数据完整性
    let expectedTotalLength = 4 + nonceLength
    guard message.count >= expectedTotalLength else {
        print("消息不完整: 期望 \(expectedTotalLength) 字节,实际 \(message.count) 字节")
        return nil
    }
    
    // 3. 提取各部分数据
    let nonce = message[4..<(4 + nonceLength)]
    let payload = message[(4 + nonceLength)...]
    
    return (Data(nonce), Data(payload))
}

这段代码典型用于安全通信协议 中,用于解析包含随机数(nonce)的加密消息格式,确保每次加密使用的随机数都是唯一的,防止重放攻击。 不,字节序转换后数据的数值会改变! 这是很多人容易混淆的地方。

让我用具体例子来说明:

数值变化的例子

swift 复制代码
let original: Int32 = 0x12345678  // 十进制: 305419896

print(String(format: "原始值: 0x%08x = %d", original, original))
// 输出: 原始值: 0x12345678 = 305419896

let swapped = original.byteSwapped

print(String(format: "交换后: 0x%08x = %d", swapped, swapped))
// 输出: 交换后: 0x78563412 = 2018915346

可以看到数值从 305419896 变成了 2018915346!

为什么数值会变化?

因为字节序改变的是字节的排列顺序,而不是数据本身:

内存布局对比

ini 复制代码
原始值 0x12345678:
大端序: 12 34 56 78
小端序: 78 56 34 12

byteSwapped 后:
大端序: 78 56 34 12 = 0x78563412
小端序: 12 34 56 78 = 0x12345678

正确的使用场景

byteSwapped 的目的是纠正字节序不匹配,而不是保持数值不变:

场景1:网络数据解析(正确用法)

swift 复制代码
// 网络发送的数据(大端序):0x00 0x00 0x01 0x00 表示 256
let networkData = Data([0x00, 0x00, 0x01, 0x00])

// 在小端序系统上解析:
let rawValue = networkData.withUnsafeBytes { 
    $0.loadUnaligned(as: Int32.self) 
}
print(rawValue) // 65536 ❌ 错误!

let correctedValue = networkData.withUnsafeBytes { 
    $0.loadUnaligned(as: Int32.self).byteSwapped 
}
print(correctedValue) // 256 ✅ 正确!

场景2:错误的转换

swift 复制代码
// 本地创建的数据(小端序)
let localValue: Int32 = 256  // 内存中: 00 01 00 00

// 错误地使用 byteSwapped
let wrongValue = localValue.byteSwapped
print(wrongValue) // 65536 ❌ 本地数据不需要交换!

// 正确的做法:只有跨平台数据才需要交换

什么时候数值会"看起来"一样?

只有在对称的字节模式时,交换前后数值可能相同:

swift 复制代码
let symmetric1: Int16 = 0x1234
let swapped1 = symmetric1.byteSwapped  // 0x3412 = 13330 ❌ 不同

let symmetric2: Int16 = 0x1212  
let swapped2 = symmetric2.byteSwapped  // 0x1212 = 4626 ✅ 相同(巧合)

let symmetric3: Int32 = 0x12344321
let swapped3 = symmetric3.byteSwapped  // 0x21433412 = 558161938 ❌ 不同

总结

关键理解:

  • byteSwapped 改变字节顺序,因此数值一定会变化
  • 它的用途是纠正因平台字节序差异导致的错误解析
  • 只有在"错误字节序"的数据上使用 byteSwapped 才能得到"正确数值"
  • 在"正确字节序"的数据上使用 byteSwapped 反而会得到错误数值

所以在你之前的代码中:

swift 复制代码
let nonceLength = message[..<4].withUnsafeBytes {
    Int($0.loadUnaligned(as: Int32.self).byteSwapped)  // 这里假设数据是大端序
}

这个操作是基于一个前提:接收到的网络数据使用大端序,而当前系统使用小端序

相关推荐
懒羊羊不懒@9 小时前
算法入门数学基础
c语言·数据结构·学习·算法
mit6.8249 小时前
[Sora] 从检查点恢复训练 | `Booster`接口 | EMA模型 | .safetensors
人工智能·算法·机器学习
CoovallyAIHub9 小时前
清华Mars Lab发布SLAM-Former:用一个Transformer统一SLAM的前端与后端(附项目地址)
深度学习·算法·计算机视觉
寂静山林9 小时前
UVa 12803 Arithmetic Expressions
算法
CoovallyAIHub9 小时前
AI基础设施新玩家:Tinker如何重新定义LLM微调工作流?
深度学习·算法·计算机视觉
xzk2012100210 小时前
洛谷 P1438 无聊的数列 题解
c++·算法·树状数组
OKkankan10 小时前
list的使用和模拟实现
数据结构·c++·算法·list
熬了夜的程序员10 小时前
【LeetCode】74. 搜索二维矩阵
线性代数·算法·leetcode·职场和发展·矩阵·深度优先·动态规划
蓝色汪洋10 小时前
oj字符矩阵
算法