ios mqttclient 使用連接、訂閱、發送、透傳

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章