Notice
Recent Posts
Recent Comments
Link
투케이2K
147. (TWOK/UTIL) [Ios/Swift] C_Broker_Mqtt_NWConnection_Client_Module : MQTT 통신 수행 유틸 파일 본문
투케이2K 유틸파일
147. (TWOK/UTIL) [Ios/Swift] C_Broker_Mqtt_NWConnection_Client_Module : MQTT 통신 수행 유틸 파일
투케이2K 2024. 12. 6. 18:24[설 명]
프로그램 : Ios / Swift
설 명 : C_Broker_Mqtt_NWConnection_Client_Module : MQTT 통신 수행 유틸 파일
[소스 코드]
import Foundation
import UIKit
// -----------------------------------------
//MARK: [네트워크 소켓 통신 사용]
import Network
// -----------------------------------------
class C_Broker_Mqtt_NWConnection_Client_Module {
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [클래스 설명]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 1. NMConnection Broker Mqtt 통신 클라이언트 모듈
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 2. NWConnection : ios 12 이상 부터 사용 가능한 애플에서 지원하는 네트워크 프레임 워크입니다
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 3. NWConnection 는 로컬 엔드포인트와 원격 엔드포인트 간의 양방향 데이터 연결 (TCP , UDP) 을 수행할 수 있습니다
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 4. NWConnection 를 사용하기 위해서는 import Network 패키지 호출 정의가 필요합니다
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 5. NWParameters 설정 가능 옵션 : tls , tcp , dtls , udp , quic , quicDatagram
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 6. 애플 공식 참고 사이트 : https://developer.apple.com/documentation/network/nwconnection
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 7. 온라인 MQTT 테스트 사이트 : https://testclient-cloud.mqtt.cool/
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 8. Aws IoT Core MQTT 디바이스 통신 프로토콜 및 port 종류 확인 :
* https://blog.naver.com/kkh0977/223580748811?trackingCode=blog_bloghome_searchlist
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* // TODO [빠른 로직 찾기 : 주석 로직 찾기]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableMqttConnect : 엔드포인트 사용해 MQTT 연결 수행
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableMqttSetting : MQTT 연결 옵션 지정 및 연결 완료 요청 수행
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : closeMqttConnect : MQTT 연결 종료 수행
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableSubscribe : 특정 토픽 TOPIC 구독 수행 실시
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : receiveData : 실시간 MQTT 메시지 수신 상태 확인
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableSendData : 실시간 MQTT 메시지 전송 수행
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
*
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [소스 코드 사용 방법]
* // --------------------------------------------------------------------------------------------------------------------------------------
DispatchQueue.main.async { // [비동기 요청]
// [변수 선언 수행]
let url = "tcp://broker.mqtt.cool:1883"
// -----------------------------------------
// [MQTT 연결 실시]
// -----------------------------------------
// 1. 정의 된 URL 과 고유한 클라이언트 ID 사용해 MQTT 연결 수행
// -----------------------------------------
// 2. receiveData 실시간 메시지 수신 상태 감지 등록 : 내려온 response 에서 topic 과 payload 를 파싱하기 위해 패킷 분해 필요
// -----------------------------------------
// 3. 특정 토픽과 QOS 설정 지정해 subscribe 구독 수행 실시 : 구독을 수행하기 위한 MQTT 버전 및 QOS 설정 패킷 전송
// -----------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module().observableMqttConnect(url: url, clientId: "twok1234"){(connectResult) in
S_Log._F_(description: "MQTT 연결 확인 수행", data: ["\(connectResult)"])
if connectResult == true {
// -----------------------------------------
// [실시간 메시지 전송 수행]
// -----------------------------------------
// 1. 특정 토픽과 QOS 설정 지정해 publish 메시지 전송 수행 실시 : MQTT 버전 및 QOS 설정 패킷 전송
// -----------------------------------------
// 2. receiveData 실시간 메시지 수신 상태에서 토픽과 페이로드 파싱해서 정보 확인
// -----------------------------------------
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { // [5초 시간 설정]
C_Broker_Mqtt_NWConnection_Client_Module().observableSendData(topic: "hello", message: "twok", qos: 0){(sendResult) in
S_Log._F_(description: "MQTT 실시간 메시지 전송 확인", data: ["\(sendResult)"])
}
}
}
else {
S_Log._F_(description: "MQTT 연결 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)"])
}
}
}
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
// -----------------------------------------------------------------------------------------
// MARK: - [전역 변수 선언]
// -----------------------------------------------------------------------------------------
private static let ACTIVITY_NAME = "C_Broker_Mqtt_NWConnection_Client_Module"
let CONNECT_TIME_OUT = 10.0 // [연결 타임 아웃 시간]
lazy var tcpOptions: NWProtocolTCP.Options = {
let options = NWProtocolTCP.Options()
options.noDelay = true // [딜레이 비활성]
options.connectionTimeout = Int(self.CONNECT_TIME_OUT) // [connection timed out]
return options
}()
lazy var parames: NWParameters = {
let parames = NWParameters(tls: nil, tcp: self.tcpOptions)
if let isOption = parames.defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {
// isOption.version = .v4 // [ipv4 방식 : 192.168.1.1]
}
parames.preferNoProxies = true
return parames
}()
static var connection: NWConnection? = nil // [MQTT 통신 수행 객체]
var workItem: DispatchWorkItem? = nil // [연결 타임 아웃 핸들러]
static var connectFlag = false // [연결 완료 플래그 값]
// -----------------------------------------------------------------------------------------
// MARK: - observableMqttConnect : 엔드포인트 사용해 MQTT 연결 수행
// -----------------------------------------------------------------------------------------
// 1. 정의 된 URL 과 고유한 클라이언트 ID 사용해 MQTT 연결 수행
// -----------------------------------------------------------------------------------------
// 2. receiveData 실시간 메시지 수신 상태 감지 등록 : 내려온 response 에서 topic 과 payload 를 파싱하기 위해 패킷 분해 필요
// -----------------------------------------------------------------------------------------
// 3. 특정 토픽과 QOS 설정 지정해 subscribe 구독 수행 실시 : 구독을 수행하기 위한 MQTT 버전 및 QOS 설정 패킷 전송
// -----------------------------------------------------------------------------------------
static var MQTT_CONNECT_LOG = ""
func observableMqttConnect(url: String, clientId: String, completion: @escaping (Bool)->()) {
/*
// -----------------------------------------
[observableSocketConnect 메소드 설명]
// -----------------------------------------
1. IP , PORT 사용해 웹 소켓 연결 수행
// -----------------------------------------
2. 호출 방법 :
C_Broker_Mqtt_NWConnection_Client_Module().observableMqttConnect(url: "tcp://broker.mqtt.cool:1883", clientId: "twok1234"){(connectResult) in
S_Log._F_(description: "MQTT 연결 확인 수행", data: ["\(connectResult)"])
if connectResult == true {
// [MQTT 연결 완료 로직 처리]
}
else {
S_Log._F_(description: "MQTT 연결 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)"])
}
}
// -----------------------------------------
3. 필요 import :
import Network
// -----------------------------------------
*/
// [변수 선언]
C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = ""
self.workItem = nil
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
// [로직 처리 수행]
DispatchQueue.main.async {
S_Log._D_(description: "MQTT 연결 수행", data: [ "URL :: \(url)", "CLIENT_ID :: \(clientId)" ])
// [인풋 데이터 널 체크]
if C_Util().stringNotNull(str: url) == true && C_Util().stringNotNull(str: clientId) == true {
// ---------------------------------------------
// MARK: [MQTT 연결 및 연결 상태 확인]
// ---------------------------------------------
// [연결 객체 생성]
C_Broker_Mqtt_NWConnection_Client_Module.connection = NWConnection(to: .url(URL(string: url)!), using: self.parames)
// [연결 상태 업데이트 핸들러 설정]
C_Broker_Mqtt_NWConnection_Client_Module.connection?.stateUpdateHandler = { state in
switch state {
case .ready:
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = "[Success] : Connection Ready State"
S_Log._W_(description: "MQTT 연결 준비 완료", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)",
"URL :: \(url)",
"CLIENT_ID :: \(clientId)"
])
// ---------------------------------------------
// MARK: [MQTT 연결 옵션 값 지정 최종 연결 완료 요청 수행]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module().observableMqttSetting(clientId: clientId){(sendResult) in
S_Log._W_(description: "MQTT 연결 완료 상태 확인", data: ["\(sendResult)"])
if sendResult == true {
// ---------------------------------------------
// MARK: [연결 완료 플래그 값 변경]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = true
// ---------------------------------------------
// MARK: [실시간 메시지 수신 상태 확인]
// ---------------------------------------------
self.receiveData()
// ---------------------------------------------
// MARK: [특정 토픽 구독 수행 실시]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module().observableSubscribe(topic: "hello", qos: 0){(sendResult) in
S_Log._W_(description: "MQTT 토픽 구독 완료 상태 확인", data: ["\(sendResult)"])
if sendResult == true {
}
else {
S_Log._E_(description: "MQTT 토픽 구독 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG)"])
}
}
// ---------------------------------------------
// [콜백 반환]
// ---------------------------------------------
completion(true)
return
}
else {
S_Log._E_(description: "MQTT 연결 완료 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG)"])
// [에러 메시지 삽입]
C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG = C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG
// [콜백 반환]
if C_Broker_Mqtt_NWConnection_Client_Module.connectFlag == false {
completion(false)
}
// [객체 초기화]
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil {
C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
}
return
}
}
case .failed(let error):
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = "[Fail] : Connection Fail State"
S_Log._E_(description: "MQTT 연결 실패", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)",
"Description :: \(error)"
])
// [콜백 반환]
if C_Broker_Mqtt_NWConnection_Client_Module.connectFlag == false {
completion(false)
}
// [객체 초기화]
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil {
C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
}
return
case .cancelled:
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = "[Cancel] : Connection Cancel State"
S_Log._E_(description: "MQTT 연결 실패", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)"
])
// [콜백 반환]
if C_Broker_Mqtt_NWConnection_Client_Module.connectFlag == false {
completion(false)
}
// [객체 초기화]
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil {
C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
}
return
default:
break
}
}
// ---------------------------------------------
// [연결 시작]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.start(queue: .global())
// ---------------------------------------------
// MARK: [MQTT 연결 타임 아웃 처리]
// ---------------------------------------------
self.workItem = DispatchWorkItem {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil && C_Broker_Mqtt_NWConnection_Client_Module.connectFlag == false {
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = "[Time Out] : Mqtt Connection TimeOut"
S_Log._E_(description: "MQTT 연결 실패", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)"
])
// [MQTT 연결 취소]
C_Broker_Mqtt_NWConnection_Client_Module.connection?.cancel()
}
}
let delay = DispatchTime.now() + self.CONNECT_TIME_OUT // [특정 시간 후에 실행]
DispatchQueue.main.asyncAfter(deadline: delay, execute: self.workItem!)
}
else {
C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG = "[Error] : Input Data Is Null"
S_Log._E_(description: "MQTT 연결 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.MQTT_CONNECT_LOG)"
])
completion(false) // [콜백 반환]
return
}
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : observableMqttSetting : MQTT 연결 옵션 지정 및 연결 완료 요청 수행
// -----------------------------------------------------------------------------------------
static var SETTING_ERROR_LOG = ""
func observableMqttSetting(clientId: String, completion: @escaping (Bool)->()) {
/*
// -----------------------------------------
[observableMqttSetting 메소드 설명]
// -----------------------------------------
1. MQTT 연결 옵션 지정 및 연결 완료 요청 수행
// -----------------------------------------
2. 호출 방법 :
C_Broker_Mqtt_NWConnection_Client_Module().observableMqttSetting(clientId: "twok1234"){(sendResult) in
S_Log._F_(description: "MQTT 연결 완료 상태 확인", data: ["\(sendResult)"])
if sendResult == true {
}
else {
S_Log._F_(description: "MQTT 연결 완료 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG)"])
}
}
// -----------------------------------------
3. 필요 import :
import Network
// -----------------------------------------
*/
// [변수 선언]
C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG = ""
// [로직 처리 수행]
DispatchQueue.global().sync {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil && C_Util().stringNotNull(str: clientId) == true {
S_Log._D_(description: "MQTT 연결 옵션 지정 및 연결 완료 요청 수행", data: nil)
/*
// ---------------------------------------------
// MARK: [최종 연결 요청 패킷 보내기]
// ---------------------------------------------
1. MQTT 버전 설명 :
- 0x03 : MQTT 3.1 : MQIsdp
- 0x04 : MQTT 3.1.1 : MQTT
- 0x05 : MQTT 5.0 : MQTT
// ---------------------------------------------
2. Connect Flags 타입 종류 :
- Bit : Name
- 7 : User Name Flag
- 6 : Password Flag
- 5 : Will Retain
- 4 : Will QoS
- 3 : Will Flag
- 2 : Clean Session
- 1 : Reserved
// ---------------------------------------------
3. Keep Alive : 클라이언트와 브로커간 연결 상태 유지 값 : 해당 값 동안 들어온 패킷이 없으면 서버는 연결 중단 상태로 인지
- Keep Alive 설정의 최대값은 65535초, 즉 18시간 12분 15초
- 60 초 : 0x3C
- 180 초 : 0xB4
// ---------------------------------------------
4. QoS : Application Message의 신뢰성을 보장하는 레벨입니다 (0, 1, 2)
- 숫자가 커질수록 높은 신뢰성을 보장하지만 그만큼의 리소스를 소모합니다
// ---------------------------------------------
*/
// ---------------------------------------------
var packet = [UInt8]()
packet.append(0x10) // [CONNECT Control Packet type]
let variableHeader: [UInt8] = [
0x00, 0x04, // [Length of "MQTT 3.1.1"]
0x4D, 0x51, 0x54, 0x54, // ["MQTT"] : "MQTT".utf8
0x04, // [Protocol Level (4 for MQTT 3.1.1)]
0x02, // [Connect Flags (Clean session)]
0x00, 0xB4 // [Keep Alive (180 seconds)] : 클라이언트와 브로커간 연결 상태 유지 값 : Hex To Dec
]
let clientIdBytes = Array(clientId.utf8) // [클라이언트 아이디]
let payload = UInt16(clientIdBytes.count).bigEndianBytes + clientIdBytes
let remainingLength = UInt8(variableHeader.count + payload.count)
packet.append(remainingLength)
packet.append(contentsOf: variableHeader)
packet.append(contentsOf: payload)
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.send(content: Data(packet), completion: .contentProcessed { error in
if let error = error {
C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG = "[Error] : Mqtt Setting Send Connection Packet Error"
S_Log._E_(description: "MQTT 연결 요청 패킷 전송 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG)",
"Description :: \(error)"
])
// [콜백 반환]
completion(false)
return
} else {
S_Log._W_(description: "MQTT 연결 요청 패킷 전송 성공", data: nil)
// [콜백 반환]
completion(true)
return
}
})
}
else {
C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG = "[Error] : Setting Input Send Message Is Null"
S_Log._E_(description: "MQTT 연결 옵션 지정 및 연결 완료 요청 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SETTING_ERROR_LOG)"
])
completion(false) // [콜백 반환]
return
}
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : closeMqttConnect : MQTT 연결 종료 수행
// -----------------------------------------------------------------------------------------
func closeMqttConnect() {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil {
S_Log._E_(description: "MQTT 연결 종료 수행 [IF]", data: nil)
C_Broker_Mqtt_NWConnection_Client_Module.connection?.cancel()
C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
}
else {
S_Log._E_(description: "MQTT 연결 종료 수행 [ELSE]", data: nil)
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : observableSubscribe : 특정 토픽 TOPIC 구독 수행 실시
// -----------------------------------------------------------------------------------------
static var SUBSCRIBE_ERROR_LOG = ""
func observableSubscribe(topic:String, qos: UInt8, completion: @escaping (Bool)->()) {
/*
// -----------------------------------------
[observableSubscribe 메소드 설명]
// -----------------------------------------
1. 특정 토픽 TOPIC 구독 수행 실시
// -----------------------------------------
2. 호출 방법 :
C_Broker_Mqtt_NWConnection_Client_Module().observableSubscribe(topic: "hello", qos: 0){(sendResult) in
S_Log._F_(description: "MQTT 토픽 구독 완료 상태 확인", data: ["\(sendResult)"])
if sendResult == true {
}
else {
S_Log._F_(description: "MQTT 토픽 구독 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG)"])
}
}
// -----------------------------------------
3. 필요 import :
import Network
// -----------------------------------------
*/
// [변수 선언]
C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG = ""
// [로직 처리 수행]
DispatchQueue.global().sync {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil && C_Util().stringNotNull(str: topic) && qos >= 0 {
S_Log._D_(description: "MQTT 특정 토픽 Subscribe 구독 수행", data: ["TOPIC :: \(topic)", "QOS :: \(qos)"])
// ---------------------------------------------
// MARK: [패킷 데이터 전송 수행]
// ---------------------------------------------
//*
var packet = [UInt8]()
// [Fixed Header] : [고정 헤더 영역]
packet.append(0x82) // [SUBSCRIBE control packet type]
// [Variable Header] : [가변 헤더 영역]
let packetIdentifier: UInt16 = 1 // [Packet Identifier (고유 ID)]
let packetIdentifierMSB = UInt8((packetIdentifier >> 8) & 0xFF)
let packetIdentifierLSB = UInt8(packetIdentifier & 0xFF)
var variableHeader: [UInt8] = [packetIdentifierMSB, packetIdentifierLSB]
// [Payload] : [메시지 전송 페이로드]
let topicBytes = self.encodeString(topic) // [Topic 이름 UTF-8 인코딩]
let qosByte = qos // [QoS Level]
var payload: [UInt8] = topicBytes + [qosByte]
// [Remaining Length]
let remainingLength = variableHeader.count + payload.count
packet.append(contentsOf: self.encodeRemainingLength(remainingLength))
// [Variable Header + Payload 추가]
packet.append(contentsOf: variableHeader)
packet.append(contentsOf: payload)
// */
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.send(content: Data(packet), completion: .contentProcessed { error in
if let error = error {
C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG = "[Error] : Mqtt Topic Send Message Error"
S_Log._E_(description: "MQTT 특정 토픽 구독 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG)",
"Description :: \(error)"
])
// [콜백 반환]
completion(false)
return
} else {
S_Log._W_(description: "MQTT 특정 토픽 구독 성공", data: [ "TOPIC :: \(topic)" ])
// [콜백 반환]
completion(true)
return
}
})
}
else {
C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG = "[Error] : Topic Input Send Message Is Null"
S_Log._E_(description: "MQTT 특정 토픽 구독 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SUBSCRIBE_ERROR_LOG)"
])
// [콜백 반환]
completion(false)
return
}
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : receiveData : 실시간 MQTT 메시지 수신 상태 확인
// -----------------------------------------------------------------------------------------
static var RECEIVE_ERROR_LOG = ""
private func receiveData() {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil {
S_Log._D_(description: "MQTT 실시간 메시지 수신 감지 수행", data: nil)
C_Broker_Mqtt_NWConnection_Client_Module.connection?.receive(minimumIncompleteLength: 1, maximumLength: 1024, completion: { data, _, isComplete, error in
// ---------------------------------------------
// MARK: [실시간 메시지 수신 확인]
// ---------------------------------------------
if let data = data, !data.isEmpty {
let message = String(data: data, encoding: .utf8) ?? ""
let emptyCheck = message.replaceAll(_string: " ", _replace: "")
if C_Util().stringNotNull(str: emptyCheck) == true {
// ---------------------------------------------
// MARK: [데이터 패킷 파싱 수행 및 토픽 , 메시지 구분 수행]
// ---------------------------------------------
let bytes = [UInt8](data)
// [PUBLISH 패킷인지 확인 (Packet Type: 0x3)]
/*
let packetType = bytes[0] >> 4
if packetType == 3 {
handlePublishPacket(data: bytes)
}
// */
// [Variable Header: Topic 이름 파싱]
let topicLength = (UInt16(bytes[2]) << 8) | UInt16(bytes[3])
let topicStartIndex = 4
let topicEndIndex = topicStartIndex + Int(topicLength)
let topic = String(bytes: bytes[topicStartIndex..<topicEndIndex], encoding: .utf8) ?? "Unknown Topic"
// [Payload: 메시지 내용]
let payloadStartIndex = topicEndIndex
let payload = String(bytes: bytes[payloadStartIndex...], encoding: .utf8) ?? "Unknown Payload"
// ---------------------------------------------
// MARK: [로그 출력 수행]
// ---------------------------------------------
S_Log._W_(description: "[Ing] ---- [MQTT 실시간 메시지 수신 감지] ---- [Ing]", data: [
"TOTAL :: \(message)",
"TOPIC :: \(topic)",
"MSG :: \(payload)"
])
// ---------------------------------------------
// MARK: [로직 분기 처리]
// ---------------------------------------------
}
}
// ---------------------------------------------
// MARK: [계속해서 실시간 메시지 수신 처리]
// ---------------------------------------------
if isComplete {
C_Broker_Mqtt_NWConnection_Client_Module.RECEIVE_ERROR_LOG = "[Error] : Connection closed by Broker"
S_Log._E_(description: "MQTT 실시간 메시지 수신 감지 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.RECEIVE_ERROR_LOG)"
])
// ---------------------------------------------
// MARK: [MQTT 연결 종료 및 객체 초기화]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.cancel()
//C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
//C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
} else if let error = error {
C_Broker_Mqtt_NWConnection_Client_Module.RECEIVE_ERROR_LOG = "[Error] : let error catch"
S_Log._E_(description: "MQTT 실시간 메시지 수신 감지 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.RECEIVE_ERROR_LOG)",
"Description :: \(error)"
])
// ---------------------------------------------
// MARK: [MQTT 연결 종료 및 객체 초기화]
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.cancel()
//C_Broker_Mqtt_NWConnection_Client_Module.connection = nil
//C_Broker_Mqtt_NWConnection_Client_Module.connectFlag = false
} else {
// ---------------------------------------------
// [계속해서 데이터 수신]
// ---------------------------------------------
self.receiveData()
}
})
}
else {
S_Log._E_(description: "MQTT 실시간 메시지 수신 감지 에러 :: connection is null", data: nil)
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : observableSendData : 실시간 MQTT 메시지 전송 수행
// -----------------------------------------------------------------------------------------
// 1. 특정 토픽과 QOS 설정 지정해 publish 메시지 전송 수행 실시 : MQTT 버전 및 QOS 설정 패킷 전송
// -----------------------------------------------------------------------------------------
// 2. receiveData 실시간 메시지 수신 상태에서 토픽과 페이로드 파싱해서 정보 확인
// -----------------------------------------------------------------------------------------
static var SEND_ERROR_LOG = ""
func observableSendData(topic:String, message:String, qos: UInt8, completion: @escaping (Bool)->()) {
/*
// -----------------------------------------
[observableSendData 메소드 설명]
// -----------------------------------------
1. 실시간 MQTT 메시지 전송 수행
// -----------------------------------------
2. 호출 방법 :
C_Broker_Mqtt_NWConnection_Client_Module().observableSendData(topic: "hello", message: "twok", qos: 0){(sendResult) in
S_Log._F_(description: "MQTT 실시간 메시지 전송 확인", data: ["\(sendResult)"])
if sendResult == true {
}
else {
S_Log._F_(description: "MQTT 실시간 메시지 전송 에러 메시지", data: ["\(C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG)"])
}
}
// -----------------------------------------
3. 필요 import :
import Network
// -----------------------------------------
*/
// [변수 선언]
C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG = ""
// [로직 처리 수행]
DispatchQueue.global().sync {
if C_Broker_Mqtt_NWConnection_Client_Module.connection != nil && C_Util().stringNotNull(str: topic)
&& C_Util().stringNotNull(str: message) == true && qos >= 0 {
S_Log._D_(description: "MQTT 클라이언트 >> 특정 토픽 메시지 전송 수행", data: ["TOPIC :: \(topic)", "QOS :: \(qos)", "MSG :: \(message)"])
// ---------------------------------------------
// MARK: [패킷 데이터 전송 수행]
// ---------------------------------------------
/*
var packet = [UInt8]()
packet.append(0x30) // [PUBLISH Control Packet type]
let topicBytes = Array(topic.utf8)
let topicLength = UInt16(topicBytes.count).bigEndianBytes
let messageBytes = Array(message.utf8)
let remainingLength = UInt8(topicLength.count + topicBytes.count + messageBytes.count)
packet.append(remainingLength)
packet.append(contentsOf: topicLength)
packet.append(contentsOf: topicBytes)
packet.append(contentsOf: messageBytes)
// */
// ---------------------------------------------
//*
var packet = [UInt8]()
let qosFlag = (qos & 0x03) << 1 // [QoS 는 고정 헤더의 2~3번째 비트에 위치]
// [PUBLISH 고정 헤더 (Control Packet Type + Flags)]
packet.append(0x30 | qosFlag)
let topicBytes = Array(topic.utf8)
let topicLength = UInt16(topicBytes.count).bigEndianBytes
let messageBytes = Array(message.utf8)
var remainingLength = topicLength.count + topicBytes.count + messageBytes.count
// [QoS가 1 이상이면 Packet Identifier가 필요]
if qos > 0 {
remainingLength += 2
}
packet.append(contentsOf: self.encodeRemainingLength(remainingLength))
packet.append(contentsOf: topicLength)
packet.append(contentsOf: topicBytes)
// [Packet Identifier (QoS 1 이상일 때 추가)]
if qos > 0 {
let packetId = UInt16.random(in: 1...UInt16.max)
packet.append(contentsOf: packetId.bigEndianBytes)
}
packet.append(contentsOf: messageBytes)
// */
// ---------------------------------------------
C_Broker_Mqtt_NWConnection_Client_Module.connection?.send(content: Data(packet), completion: .contentProcessed { error in
if let error = error {
C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG = "[Error] : Mqtt Publish Send Message Error"
S_Log._E_(description: "MQTT 실시간 메시지 전송 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG)",
"Description :: \(error)"
])
completion(false) // [콜백 반환]
return
} else {
S_Log._W_(description: "MQTT 실시간 메시지 전송 성공", data: [
"message :: \(message)"
])
completion(true) // [콜백 반환]
return
}
})
}
else {
C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG = "[Error] : Publish Input Send Message Is Null"
S_Log._E_(description: "MQTT 실시간 메시지 전송 에러", data: [
"M_LOG :: \(C_Broker_Mqtt_NWConnection_Client_Module.SEND_ERROR_LOG)"
])
completion(false) // [콜백 반환]
return
}
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [유틸 파일] : Remaining Length 인코딩
// -----------------------------------------------------------------------------------------
private func encodeRemainingLength(_ length: Int) -> [UInt8] {
var value = length
var encodedBytes: [UInt8] = []
repeat {
var digit = UInt8(value % 128)
value /= 128
if value > 0 {
digit |= 0x80
}
encodedBytes.append(digit)
} while value > 0
return encodedBytes
}
// -----------------------------------------------------------------------------------------
// MARK: - [유틸 파일] : UTF-8 문자열을 MQTT 형식으로 인코딩
// -----------------------------------------------------------------------------------------
private func encodeString(_ string: String) -> [UInt8] {
let utf8Bytes = Array(string.utf8)
let length = UInt16(utf8Bytes.count)
return [UInt8(length >> 8), UInt8(length & 0xFF)] + utf8Bytes
}
} // [클래스 종료]
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [UInt16] : [Extension]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
private extension UInt16 {
var bigEndianBytes: [UInt8] {
return [
UInt8((self >> 8) & 0xFF),
UInt8(self & 0xFF)
]
}
}
반응형
'투케이2K 유틸파일' 카테고리의 다른 글
Comments