字节序
- 涉及到网络传输的时候,就会涉及到字节序的问题。App和后台传输,一般采用Https协议(XCode7之后默认就是Https的,Http的要逐渐替代掉才好)。这种场景一般要进行Json格式的解析,所以字节序的问题一般不会遇到。并且Https的传输和Json解析一般也会采用比较出名的第三方库,方案比较成熟,不需要特意考虑字节序的问题,比较省心。
- 目前的智能硬件,一般都是采用蓝牙协议。iOS框架提供了一些回调函数,通用的广播字段。但是具体的协议由App开发者和嵌入式开发者协商和联调。这个时候是16进制的NSData数据交换(二进制流,一般都用16进制表示),那么就必须要考虑字节序的问题了
基本概念
网络序 === 大端字节序 === bigEndian
主机序 === 小端字节序 === littleEndian
一般情况下,网络序都是大端字节序的。大多数的主机序都是小端序的,但也有例外,比如PowerPC体系的微机是大端序的,比较另类。
基本转换
- 主机序 转 网络序 Host -> Network
/// Returns the big-endian representation of the integer, changing the
/// byte order if necessary.
public var bigEndian: UInt16 { get }
本地数据转好之后,通过网络或者蓝牙传输
- 网络序 转 主机序 Network -> Host
/// Creates an integer from its big-endian representation, changing the
/// byte order if necessary.
public init(bigEndian value: UInt16)
接收来自网络或者蓝牙的数据,转换之后,在本地使用
常见类型
UInt8: 1个字节,不需要考虑字节序
String: 通过某种编码方式转化为NSData,不需要考虑字节序
UInt16: 比如地区编码等,需要考虑字节序
UInt32: 比如秒为单位的时间戳,需要考虑字节序
UInt64: 比如毫秒为单位的时间戳,需要考虑字节序
实际例子1 === Network -> Host
从蓝牙设备读取记录,然后将网络序转换为主机序,解析为电压,称重,电阻等数据。
import Foundation
extension HistoryRecord {
public var powerValue: Double {
return (Double(power) * 0.01) // 系数0.01V
}
public var weightValue: Double {
return (Double(weight) * 0.01) // 系数0.01kg
}
public var impedanceValue: Double {
return (Double(impedance) * 0.01) // 系数0.01Ω
}
public var utcDescription: String {
let utcDate = NSDate(timeIntervalSince1970: NSTimeInterval(utc))
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter.stringFromDate(utcDate)
}
}
extension HistoryRecord: CustomStringConvertible {
public var description: String {
return [
"\t记录序列: \(sequence)",
"\t称重模式: \(mode)",
"\t电量: \(powerValue) (V)",
"\tUTC: \(utcDescription)",
"\t称重值: \(weightValue) (kg)",
"\t阻抗值: \(impedanceValue) (Ω)",
].joinWithSeparator("\n")
}
}
public struct HistoryRecord {
public let sequence: UInt8
let mode: UInt8
let power: UInt16
let utc: UInt32
let weight: UInt16
let impedance: UInt16
private let sequenceLength = 1
private let modeLength = 1
private let powerLength = 2
private let utcLength = 4
private let weightLength = 2
private let impedanceLength = 2
public init() {
sequence = 0
mode = 0
power = 0
utc = 0
weight = 0
impedance = 0
}
public init?(_ data: NSData) {
print("=== HistoryRecord === raw data: \(data)")
let needLength = sequenceLength + modeLength + powerLength + utcLength + weightLength + impedanceLength
guard data.length == needLength else {
print("=== HistoryRecord === check lenght fail: \(data.length)")
return nil
}
var bufferUInt8: UInt8
var bufferUInt16: UInt16
var bufferUInt32: UInt32
bufferUInt8 = 0
data.getBytes(&bufferUInt8, range: NSMakeRange(0, sequenceLength))
sequence = bufferUInt8
bufferUInt8 = 0
data.getBytes(&bufferUInt8, range: NSMakeRange(sequenceLength, modeLength))
mode = bufferUInt8
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength, powerLength))
power = UInt16(bigEndian: bufferUInt16)
bufferUInt32 = 0
data.getBytes(&bufferUInt32, range: NSMakeRange(sequenceLength + modeLength + powerLength, utcLength))
utc = UInt32(bigEndian: bufferUInt32)
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength + powerLength + utcLength, weightLength))
weight = UInt16(bigEndian: bufferUInt16)
bufferUInt16 = 0
data.getBytes(&bufferUInt16, range: NSMakeRange(sequenceLength + modeLength + powerLength + utcLength + weightLength, impedanceLength))
impedance = UInt16(bigEndian: bufferUInt16)
}
}
实际例子2 === Host -> Network
本地进行一些配置,然后将主机序转换为网络序,通过蓝牙传输出去
import Foundation
extension ScreenConfig {
public var data: NSData {
let payloadData = NSMutableData()
var bufferUInt8: UInt8
var bufferUInt16: UInt16
var bufferUInt32: UInt32
bufferUInt8 = unit
payloadData.appendBytes(&bufferUInt8, length: 1)
bufferUInt16 = outDoorFreq.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
bufferUInt16 = inDoorFreq.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
bufferUInt16 = cityId.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
bufferUInt8 = timeSyncFreq
payloadData.appendBytes(&bufferUInt8, length: 1)
bufferUInt16 = timeZone.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
bufferUInt32 = baseTime.bigEndian
payloadData.appendBytes(&bufferUInt32, length: 4)
bufferUInt8 = weatherPatten
payloadData.appendBytes(&bufferUInt8, length: 1)
bufferUInt16 = savePowerStart.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
bufferUInt16 = savePowerEnd.bigEndian
payloadData.appendBytes(&bufferUInt16, length: 2)
return payloadData.copy() as! NSData
}
}
extension ScreenConfig: CustomStringConvertible{
public var description: String {
return [
"\tunit: \(unit)",
"\toutDoorFreq: \(outDoorFreq)",
"\tinDoorFreq: \(inDoorFreq)",
"\tcityId: \(cityId)",
"\ttimeSyncFreq: \(timeSyncFreq)",
"\ttimeZone: \(timeZone)",
"\tbaseTime: \(baseTime)",
"\tweatherPatten: \(weatherPatten)",
"\tsavePowerStart: \(savePowerStart)",
"\tsavePowerEnd: \(savePowerEnd)",
].joinWithSeparator("\n")
}
}
public struct ScreenConfig {
let unit: UInt8
let outDoorFreq: UInt16 // 单位:分钟
let inDoorFreq: UInt16 // 单位:分钟
let cityId: UInt16
let timeSyncFreq: UInt8
let timeZone: UInt16
let baseTime: UInt32
let weatherPatten: UInt8
let savePowerStart: UInt16
let savePowerEnd: UInt16
public init(unit: UInt8 = 0x00, outDoorFreq: UInt16 = 0x003C, inDoorFreq: UInt16 = 0x0005, cityId: UInt16 = 0x0002, timeSyncFreq: UInt8 = 0x07, timeZone: UInt16 = 0x0008, baseTime: UInt32 = 0x0000_0000, weatherPatten: UInt8 = 0x01, savePowerStart: UInt16 = 0x0008, savePowerEnd: UInt16 = 0x0008) {
self.unit = unit
self.outDoorFreq = outDoorFreq
self.inDoorFreq = inDoorFreq
self.cityId = cityId
self.timeSyncFreq = timeSyncFreq
self.timeZone = timeZone
self.baseTime = baseTime
self.weatherPatten = weatherPatten
self.savePowerStart = savePowerStart
self.savePowerEnd = savePowerEnd
}
}