216 lines
6.2 KiB
Swift
216 lines
6.2 KiB
Swift
//
|
|
// Frame.swift
|
|
// CocoaMQTT
|
|
//
|
|
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
|
// Copyright (c) 2015 emqx.io. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Quality of Service levels
|
|
@objc public enum CocoaMQTTQoS: UInt8, CustomStringConvertible {
|
|
/// At most once delivery
|
|
case qos0 = 0
|
|
|
|
/// At least once delivery
|
|
case qos1
|
|
|
|
/// Exactly once delivery
|
|
case qos2
|
|
|
|
/// !!! Used SUBACK frame only
|
|
case FAILURE = 0x80
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case .qos0: return "qos0"
|
|
case .qos1: return "qos1"
|
|
case .qos2: return "qos2"
|
|
case .FAILURE: return "Failure"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CocoaMQTTQoS: Comparable {
|
|
|
|
public static func < (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
|
return lhs.rawValue < rhs.rawValue
|
|
}
|
|
|
|
public static func <= (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
|
return lhs.rawValue <= rhs.rawValue
|
|
}
|
|
|
|
public static func > (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
|
return lhs.rawValue > rhs.rawValue
|
|
}
|
|
|
|
public static func >= (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
|
return lhs.rawValue >= rhs.rawValue
|
|
}
|
|
}
|
|
|
|
/// MQTT Frame Type
|
|
enum FrameType: UInt8 {
|
|
case reserved = 0x00
|
|
case connect = 0x10
|
|
case connack = 0x20
|
|
case publish = 0x30
|
|
case puback = 0x40
|
|
case pubrec = 0x50
|
|
case pubrel = 0x60
|
|
case pubcomp = 0x70
|
|
case subscribe = 0x80
|
|
case suback = 0x90
|
|
case unsubscribe = 0xA0
|
|
case unsuback = 0xB0
|
|
case pingreq = 0xC0
|
|
case pingresp = 0xD0
|
|
case disconnect = 0xE0
|
|
case auth = 0xF0
|
|
}
|
|
|
|
/// The frame can be initialized with a bytes
|
|
protocol InitialWithBytes {
|
|
|
|
init?(packetFixedHeaderType: UInt8, bytes: [UInt8])
|
|
}
|
|
|
|
/// MQTT Frame protocol
|
|
protocol Frame {
|
|
|
|
/// Each MQTT Control Packet contains a fixed header
|
|
/// MQTT 3.1.1
|
|
var packetFixedHeaderType: UInt8 {get set}
|
|
/// MQTT 5.0
|
|
func fixedHeader() -> [UInt8]
|
|
|
|
/// Some types of MQTT Control Packets contain a variable header component
|
|
/// MQTT 3.1.1
|
|
func variableHeader() -> [UInt8]
|
|
|
|
/// MQTT 5.0
|
|
func variableHeader5() -> [UInt8]
|
|
|
|
/// MQTT 5.0 The last field in the Variable Header of the CONNECT, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, DISCONNECT, and AUTH packet is a set of Properties. In the CONNECT packet there is also an optional set of Properties in the Will Properties field with the Payload.
|
|
func properties() -> [UInt8]
|
|
|
|
/// Some MQTT Control Packets contain a payload as the final part of the packet
|
|
/// MQTT 3.1.1
|
|
func payload() -> [UInt8]
|
|
|
|
/// MQTT 5.0
|
|
func payload5() -> [UInt8]
|
|
|
|
/// fixedHeader + variableHeader + properties + payload
|
|
func allData() -> [UInt8]
|
|
}
|
|
|
|
extension Frame {
|
|
|
|
/// Pack struct to binary
|
|
func bytes(version: String) -> [UInt8] {
|
|
|
|
if version == "5.0" {
|
|
let fixedHeader = self.fixedHeader()
|
|
let variableHeader5 = self.variableHeader5()
|
|
let payload5 = self.payload5()
|
|
let properties = self.properties()
|
|
let len5 = UInt32(variableHeader5.count + properties.count + payload5.count)
|
|
|
|
printDebug("==========================MQTT 5.0==========================")
|
|
printDebug("packetFixedHeaderType \(packetFixedHeaderType)")
|
|
printDebug("fixedHeader \(fixedHeader)")
|
|
printDebug("remainingLen(len: len) \(remainingLen(len: len5))")
|
|
printDebug("variableHeader \(variableHeader5)")
|
|
printDebug("properties \(properties)")
|
|
printDebug("payload \(payload5)")
|
|
printDebug("=============================================================")
|
|
|
|
return [packetFixedHeaderType] + remainingLen(len: len5) + variableHeader5 + properties + payload5
|
|
} else {
|
|
|
|
let variableHeader = self.variableHeader()
|
|
let payload = self.payload()
|
|
|
|
let len = UInt32(variableHeader.count + payload.count)
|
|
|
|
printDebug("=========================MQTT 3.1.1=========================")
|
|
printDebug("packetFixedHeaderType \(packetFixedHeaderType)")
|
|
printDebug("remainingLen(len: len) \(remainingLen(len: len))")
|
|
printDebug("variableHeader \(variableHeader)")
|
|
printDebug("payload \(payload)")
|
|
printDebug("=============================================================")
|
|
|
|
return [packetFixedHeaderType] + remainingLen(len: len) + variableHeader + payload
|
|
}
|
|
|
|
}
|
|
|
|
private func remainingLen(len: UInt32) -> [UInt8] {
|
|
var bytes: [UInt8] = []
|
|
var digit: UInt8 = 0
|
|
|
|
var len = len
|
|
repeat {
|
|
digit = UInt8(len % 128)
|
|
len /= 128
|
|
// if there are more digits to encode, set the top bit of this digit
|
|
if len > 0 {
|
|
digit |= 0x80
|
|
}
|
|
bytes.append(digit)
|
|
} while len > 0
|
|
|
|
return bytes
|
|
}
|
|
}
|
|
|
|
/// Fixed Header Attributes
|
|
extension Frame {
|
|
|
|
/// The Fixed Header consist of the following attritutes
|
|
///
|
|
/// +---------+----------+-------+--------+
|
|
/// | 7 6 5 4 | 3 | 2 1 | 0 |
|
|
/// +---------+----------+-------+--------+
|
|
/// | Type | DUP flag | QoS | RETAIN |
|
|
/// +-------------------------------------+
|
|
|
|
/// The type of the Frame
|
|
var type: FrameType {
|
|
return FrameType(rawValue: packetFixedHeaderType & 0xF0)!
|
|
}
|
|
|
|
/// Dup flag
|
|
var dup: Bool {
|
|
get {
|
|
return ((packetFixedHeaderType & 0x08) >> 3) == 0 ? false : true
|
|
}
|
|
set {
|
|
packetFixedHeaderType = (packetFixedHeaderType & 0xF7) | (newValue.bit << 3)
|
|
}
|
|
}
|
|
|
|
/// Qos level
|
|
var qos: CocoaMQTTQoS {
|
|
get {
|
|
return CocoaMQTTQoS(rawValue: (packetFixedHeaderType & 0x06) >> 1)!
|
|
}
|
|
set {
|
|
packetFixedHeaderType = (packetFixedHeaderType & 0xF9) | (newValue.rawValue << 1)
|
|
}
|
|
}
|
|
|
|
/// Retained flag
|
|
var retained: Bool {
|
|
get {
|
|
return (packetFixedHeaderType & 0x01) == 0 ? false : true
|
|
}
|
|
set {
|
|
packetFixedHeaderType = (packetFixedHeaderType & 0xFE) | newValue.bit
|
|
}
|
|
}
|
|
}
|