1.ios 智能家居 mqtt 使用場景
mqtt mqtt
zigbeen -> 網關 -> 服務器 -> app
2. mqtt client 使用流程
1. Podfile文件內容, mqtt client 安裝依賴
platform :ios,'9.0'
use_frameworks!
target 'MqttClient' do
inhibit_all_warnings!
pod 'AFNetworking', '~>3.0'
pod 'MQTTClient'
end
pod install
2、 創建連接 、設置 mqttSession 代理
- (IBAction)connectionMqtt:(id)sender {
MQTTCFSocketTransport *transport = (MQTTCFSocketTransport *)self.mqttSession.transport;
/*MQTTCFSocketEncoder *enc = [transport valueForKey:@"encoder"];
BOOL socketClosed = enc.stream.streamStatus<NSStreamStatusOpening || enc.stream.streamStatus>NSStreamStatusAtEnd;*/
BOOL connected = self.mqttSession.status == MQTTSessionStatusConnected;
BOOL connecting = self.mqttSession.status == MQTTSessionStatusConnecting;
if ((![self.mqttSession.userName isEqualToString:self.uid] || ![self.mqttSession.password isEqualToString:self.token]) || !(connecting || connected))
{
connected = NO;
[self internalClose];
if (!self.mqttSession)
{
self.mqttSession = [[MQTTSession alloc] init];
transport = [[MQTTCFSocketTransport alloc] init];
transport.host = kMQTTHost;
transport.port = kMQTTPort;
self.mqttSession.transport = transport;
self.mqttSession.keepAliveInterval = 5;
self.mqttSession.delegate = self;
[MQTTLog setLogLevel:DDLogLevelDebug];
}
self.mqttSession.userName = self.uid;
self.mqttSession.clientId = [NSString stringWithFormat:@"app:%@", self.uid];
self.mqttSession.password = self.token;
[self.mqttSession connect];
}
}
3、 連接狀態監聽
-(void)connected:(MQTTSession *)session{} 連接成功回調,訂閱topic
-(void)connectionRefused:(MQTTSession *)session error:(NSError *)error
連接被拒絕
-(void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error{}
連接狀態回調
///關閉MQTT服務器的連接。@note 注意,現時不能統一在這裏移除隊列任務,否則會造成沒有連接前添加的任務全部被移除了。
- (void)internalClose
{
NSLog(@"%@",@"關閉mqtt連接");
[self.mqttSession disconnect];
// self.subscribed = NO;
}
/*連接成功回調*/
-(void)connected:(MQTTSession *)session{
NSLog(@"%@",@"連接成功");
UInt16 pid = 0;
NSString* subTopic=[NSString stringWithFormat:@"/%@/rpc/reply", self.uid];
pid = [self.mqttSession subscribeToTopic:subTopic atLevel:MQTTQosLevelExactlyOnce subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
// block(error, gQoss);
NSLog(@"訂閱回調: %@--%@",error,gQoss);
}];
}
-(void)connectionClosed:(MQTTSession *)session{
self.subscribed = NO;
}
-(void)connectionRefused:(MQTTSession *)session error:(NSError *)error
{
switch (error.code)
{
case MQTTSessionErrorConnackBadUsernameOrPassword:
case MQTTSessionErrorConnackNotAuthorized:
NSLog(@"%@",@"Mqtt 鑑權錯誤... 連接被拒絕");
break;
default:
NSLog(@"%@",@"Mqtt 連接被拒絕");
break;
}
}
/*連接狀態回調*/
-(void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error{
if (eventCode == MQTTSessionEventConnected) {
NSLog(@"2222222 鏈接MQTT 成功");
}else if (eventCode == MQTTSessionEventConnectionRefused) {
NSLog(@"MQTT拒絕鏈接");
}else if (eventCode == MQTTSessionEventConnectionClosed){
NSLog(@"MQTT鏈接關閉");
}else if (eventCode == MQTTSessionEventConnectionError){
NSLog(@"MQTT 鏈接錯誤");
}else if (eventCode == MQTTSessionEventProtocolError){
NSLog(@"MQTT 不可接受的協議");
}else{//MQTTSessionEventConnectionClosedByBroker
NSLog(@"MQTT鏈接 其他錯誤");
}
if (error) {
NSLog(@"鏈接報錯 -- %@",error);
}
}
3、 訂閱topic以後服務器 推送消息到app回調接口
-(void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid{}
/*連接成功回調*/
-(void)connected:(MQTTSession *)session{
NSLog(@"%@",@"連接成功");
UInt16 pid = 0;
NSString* subTopic=[NSString stringWithFormat:@"/%@/rpc/reply", self.uid];
pid = [self.mqttSession subscribeToTopic:subTopic atLevel:MQTTQosLevelExactlyOnce subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
// block(error, gQoss);
NSLog(@"訂閱回調: %@--%@",error,gQoss);
}];
}
/*收到消息*/
-(void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid{
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"mqtt result=%@",result);
if (![result isKindOfClass:NSDictionary.class]) return;
NSNumber *msgId = result[@"msgId"]?:result[@"msgid"];
NSString *func = [result[kFunc] isKindOfClass:NSString.class] ? result[kFunc] : @"不是字符串";
NSLog(@"收到mqtt消息: %@---%@",msgId,func);
}
4. app到服務器
主題名: NSString* const MQTTServerTopic = @"/request/app/func";
app 到服務器 透傳到網關
主題名: NSString* const MQTTGWTopic = @"/type/gwid/call";
[self.mqttSession publishData:payload onTopic:MQTTServerTopic retain:NO qos:MQTTQosLevelExactlyOnce publishHandler:^(NSError *error) {
NSLog(@"%@",@"發送獲取設備列表");
completion(nil,nil,nil);
}];
發送過去以後再 3中 收到消息
- (IBAction)getDevicesList:(id)sender {
[self performServerFunc:@"getAllBindDevice" withParams:@{@"msgtype":@"request"} completion:^(NSError * _Nullable error, BOOL b, id _Nullable rs) {
NSLog(@"%@",@"完成");
}];
}
#pragma mark MQTT服務器相關接口。
- (void )performServerFunc:(NSString *)func withParams:(NSDictionary *)funcParams completion:(void (^)(NSError * _Nullable, BOOL, id _Nullable))completion
{
NSAssert(func.length, @"%s__%d__執行的功能不能爲空", __FILE__, __LINE__);
NSMutableDictionary *newParam = [NSMutableDictionary dictionaryWithDictionary:funcParams ?: @{}];
newParam[kFunc] = func;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
newParam[@"uid"] = self.uid;
newParam[@"msgId"] = @([self nextMsgId]);
NSData *payload=[NSJSONSerialization dataWithJSONObject:newParam options:0 error:nil];
#pragma clang diagnostic pop
UInt16 pid = [self.mqttSession publishData:payload onTopic:MQTTServerTopic retain:NO qos:MQTTQosLevelExactlyOnce publishHandler:^(NSError *error) {
NSLog(@"%@",@"發送獲取設備列表");
if(error){
NSLog(@"發送失敗 - %@",error);
}else{
NSLog(@"發送成功");
}
completion(nil,nil,nil);
}];
}
5. 取消訂閱
[self.mqttSession unsubscribeTopic:subTopic unsubscribeHandler:^(NSError *error) {
NSLog(@"取消訂閱: %@",error);
}];
- (IBAction)cancleSubsribe:(id)sender {
NSString* subTopic=[NSString stringWithFormat:@"/%@/rpc/reply", self.uid];
UInt16 pid = [self.mqttSession unsubscribeTopic:subTopic unsubscribeHandler:^(NSError *error) {
if (error) {
NSLog(@"取消訂閱失敗--%@",error);
}else{
NSLog(@"取消訂閱成功");
}
}];
}
完整代碼:
//
// ViewController.m
// MqttClient
//
#import "ViewController.h"
#import <MQTTClient/MQTTClient.h>
#import "KDSHttpManager.h"
#import "KDSHttpManager+Login.h"
#import "KDSTool.h"
#define kBaseURL @""
#define kMQTTHost @""
#pragma mark - 主題名
NSString* const MQTTServerTopic = @"/request/app/func";
NSString* const MQTTGWTopic = @"/type/%@/call";
//#define MQTTGWTopic(gwuuid) [NSString stringWithFormat:MQTTGWTopic, gwuuid]///<發佈到網關的主題宏。
// %@ 用對應 網關 sn 替代
#define kMQTTPort 1883
static NSString * const kFunc = @"func";
@interface ViewController ()<MQTTSessionDelegate>
@property (nonatomic, strong) MQTTSession *mqttSession;
@property(nonatomic,copy) NSString* uid;
@property(nonatomic,copy) NSString* token;
@property (nonatomic, assign) BOOL subscribed;
@property (nonatomic, assign) NSInteger msgId;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_msgId=1;
}
// 1. 創建連接 、設置 mqttSession 代理
- (IBAction)connectionMqtt:(id)sender {
MQTTCFSocketTransport *transport = (MQTTCFSocketTransport *)self.mqttSession.transport;
/*MQTTCFSocketEncoder *enc = [transport valueForKey:@"encoder"];
BOOL socketClosed = enc.stream.streamStatus<NSStreamStatusOpening || enc.stream.streamStatus>NSStreamStatusAtEnd;*/
BOOL connected = self.mqttSession.status == MQTTSessionStatusConnected;
BOOL connecting = self.mqttSession.status == MQTTSessionStatusConnecting;
if ((![self.mqttSession.userName isEqualToString:self.uid] || ![self.mqttSession.password isEqualToString:self.token]) || !(connecting || connected))
{
connected = NO;
[self internalClose];
if (!self.mqttSession)
{
self.mqttSession = [[MQTTSession alloc] init];
transport = [[MQTTCFSocketTransport alloc] init];
transport.host = kMQTTHost;
transport.port = kMQTTPort;
self.mqttSession.transport = transport;
self.mqttSession.keepAliveInterval = 5;
self.mqttSession.delegate = self;
[MQTTLog setLogLevel:DDLogLevelDebug];
}
self.mqttSession.userName = self.uid;
self.mqttSession.clientId = [NSString stringWithFormat:@"app:%@", self.uid];
self.mqttSession.password = self.token;
[self.mqttSession connect];
}
}
///關閉MQTT服務器的連接。@note 注意,現時不能統一在這裏移除隊列任務,否則會造成沒有連接前添加的任務全部被移除了。
- (void)internalClose
{
NSLog(@"%@",@"關閉mqtt連接");
[self.mqttSession disconnect];
// self.subscribed = NO;
}
/* 2. 連接成功回調*/
-(void)connected:(MQTTSession *)session{
NSLog(@"%@",@"連接成功");
UInt16 pid = 0;
NSString* subTopic=[NSString stringWithFormat:@"/%@/rpc/reply", self.uid];
pid = [self.mqttSession subscribeToTopic:subTopic atLevel:MQTTQosLevelExactlyOnce subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) {
// block(error, gQoss);
NSLog(@"訂閱回調: %@--%@",error,gQoss);
}];
}
-(void)connectionClosed:(MQTTSession *)session{
self.subscribed = NO;
}
-(void)connectionRefused:(MQTTSession *)session error:(NSError *)error
{
switch (error.code)
{
case MQTTSessionErrorConnackBadUsernameOrPassword:
case MQTTSessionErrorConnackNotAuthorized:
NSLog(@"%@",@"Mqtt 鑑權錯誤... 連接被拒絕");
break;
default:
NSLog(@"%@",@"Mqtt 連接被拒絕");
break;
}
}
/*連接狀態回調*/
-(void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error{
if (eventCode == MQTTSessionEventConnected) {
NSLog(@"2222222 鏈接MQTT 成功");
}else if (eventCode == MQTTSessionEventConnectionRefused) {
NSLog(@"MQTT拒絕鏈接");
}else if (eventCode == MQTTSessionEventConnectionClosed){
NSLog(@"MQTT鏈接關閉");
}else if (eventCode == MQTTSessionEventConnectionError){
NSLog(@"MQTT 鏈接錯誤");
}else if (eventCode == MQTTSessionEventProtocolError){
NSLog(@"MQTT 不可接受的協議");
}else{//MQTTSessionEventConnectionClosedByBroker
NSLog(@"MQTT鏈接 其他錯誤");
}
if (error) {
NSLog(@"鏈接報錯 -- %@",error);
}
}
/* 3. 收到消息*/
-(void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid{
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"mqtt result=%@",result);
if (![result isKindOfClass:NSDictionary.class]) return;
NSNumber *msgId = result[@"msgId"]?:result[@"msgid"];
NSString *func = [result[kFunc] isKindOfClass:NSString.class] ? result[kFunc] : @"不是字符串";
NSLog(@"收到mqtt消息: %@---%@",msgId,func);
}
// 用戶登錄獲取 token ,uid
- (IBAction)login:(id)sender {
int source = 1;
NSString *username = @"8615875568850";
NSString *passWord = @"a123456";
[[KDSHttpManager sharedManager] login:source username:username password:passWord success:^(KDSUser * _Nonnull user) {
NSLog(@"userid==%@",user.uid);
NSLog(@"token==%@",user.token);
self.uid= user.uid;
self.token = user.token;
// NSString *account = [KDSTool getDefaultLoginAccount];
[KDSTool setDefaultLoginAccount:username];
[KDSTool setDefaultLoginPassWord:passWord];
// 設置token
[KDSHttpManager sharedManager].token = user.token;
} error:^(NSError * _Nonnull error) {
NSString *msg;
msg = error.localizedDescription;
NSLog(@"error=%@",msg);
} failure:^(NSError * _Nonnull error) {
NSLog(@"%@",@"failure");
}];
}
// 4. 獲取消息 app 到服務器
- (IBAction)getDevicesList:(id)sender {
[self performServerFunc:@"getAllBindDevice" withParams:@{@"msgtype":@"request"} completion:^(NSError * _Nullable error, BOOL b, id _Nullable rs) {
NSLog(@"%@",@"完成");
}];
}
#pragma mark MQTT服務器相關接口。
- (void )performServerFunc:(NSString *)func withParams:(NSDictionary *)funcParams completion:(void (^)(NSError * _Nullable, BOOL, id _Nullable))completion
{
NSAssert(func.length, @"%s__%d__執行的功能不能爲空", __FILE__, __LINE__);
NSMutableDictionary *newParam = [NSMutableDictionary dictionaryWithDictionary:funcParams ?: @{}];
newParam[kFunc] = func;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
newParam[@"uid"] = self.uid;
newParam[@"msgId"] = @([self nextMsgId]);
NSData *payload=[NSJSONSerialization dataWithJSONObject:newParam options:0 error:nil];
#pragma clang diagnostic pop
UInt16 pid = [self.mqttSession publishData:payload onTopic:MQTTServerTopic retain:NO qos:MQTTQosLevelExactlyOnce publishHandler:^(NSError *error) {
NSLog(@"%@",@"發送獲取設備列表");
if(error){
NSLog(@"發送失敗 - %@",error);
}else{
NSLog(@"發送成功");
}
completion(nil,nil,nil);
}];
}
///獲取msgId時加鎖。
- (NSInteger)nextMsgId
{
@synchronized (self) {
NSInteger mid = self.msgId;
self.msgId ++;
return mid;
}
}
// 5. 取消訂閱
- (IBAction)cancleSubsribe:(id)sender {
NSString* subTopic=[NSString stringWithFormat:@"/%@/rpc/reply", self.uid];
UInt16 pid = [self.mqttSession unsubscribeTopic:subTopic unsubscribeHandler:^(NSError *error) {
if (error) {
NSLog(@"取消訂閱失敗--%@",error);
}else{
NSLog(@"取消訂閱成功");
}
}];
}
@end
源碼下載地址 :https://download.csdn.net/download/dreams_deng/12572615