iOS MQTT(MQTTClient) 车联网之实战篇

    最近公司在做车联网,想到用MQTT实现多通道连接订阅主题,能够高效的进行消息传递。实现手机端、硬件、网站、后台多端同步。话不多说,上干货。

    首先

    pod 'MQTTClient'

    pod 'MQTTClient/Min'

    pod 'MQTTClient/Manager'

    pod 'MQTTClient/Websocket' 

    这里采用的是MQTTClient开源库开发。如果后台的MQTT服务用的ws,则还需要倒入pod 'MQTTClient/Websocket',此处根据自身需要pod即可。

    一般情况下我们用MQTTCFSocketTransport设置ip地址以及端口号即可,但是如果像上面的提到的用ws的情况,则需要用到MQTTWebsocketTransport。我的项目就是用的MQTTWebsocketTransport,以下代码可以作为参考。

创建了一个MQTTManager的管理类.h

#import

// 头文件以及配置信息

#import "MQTTHeaderConfig.h"

NS_ASSUME_NONNULL_BEGIN

@protocolMQTTManagerDelegate

@optional

// 连接状态改变回调

-(void)MQTTManagerSessionStatus:(MQTTSessionStatus)status;

// 订阅主题成功|失败回调

-(void)MQTTManagerSubscribeTopicSuccessOrNot:(BOOL)YesOrNot Topic:(NSString*_Nullable)topic;

// 取消订阅主题成功|失败回调

-(void)MQTTManagerCancerSubscribeTopicSuccessOrNot:(BOOL)YesOrNot Topic:(NSString*_Nullable)topic;

// 收到订阅消息

-(void)MQTTManagerReceiveMessage:(MQTTSession*)session data:(NSData*)data onTopic:(NSString*)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsignedint)mid;

@end

@interfaceMQTTManager :NSObject

// 单例

+ (instancetype)manager;

// 配置MQTT

- (void)BindWithCliendId:(NSString*)cliendId SetDelegate:(id)delegate;

// 生成cliendId 的方法

- (NSString*)GetCliendId:(NSString*)userid;

// 订阅主题

- (void)subscribeTopic:(NSString*)topic;

// 取消订阅主题

- (void)unsubscribeTopic:(NSString*)topic;

// 向对应主题发布消息

- (void)sendDataToTopic:(NSString*)topic dict:(NSDictionary*)dict;

- (void)sendDataToTopic:(NSString*)topic SendMsg:(NSString*)msg SendMsgType:(NSString*)type;

// 主动断开连接

- (void)disconnect;

// 重新连接

- (void)MySessionReConnect;

// 代理

@property(nonatomic,weak)iddelegate;

@property(nonatomic)MQTTSession *__nullable mySession;

@property(nonatomic)MQTTSessionManager*sessionManager;

// 是否连接

@property(nonatomic,assign)BOOL isDiscontent;

// (客户端 id,用于区别客户端)

@property(nonatomic,copy)NSString *_Nullable cliendId;

// 发送的主题

@property(nonatomic,copy)NSString *_Nullable MQTTSendTopic;

// 发送的内容

@property(nonatomic,copy)NSString *_Nullable MQTTSendMsg;

// 发送内容类型

@property(nonatomic,copy)NSString *_Nullable MQTTSendMsgType;

// 客户端确定通知服务器全局主题上线成功,服务器收到的标志 默认为no

@property(nonatomic,assign)BOOL serverDidReceive;

@end

NS_ASSUME_NONNULL_END

.m文件

#import "MQTTManager.h"

#import

@interface MQTTManager()

// 当前 订阅过主题结合

@property(nonatomic,strong)NSMutableArray *subArray;

@end

@implementation MQTTManager

#pragma mark - 懒加载

-(NSMutableArray *)subArray{

    if(!_subArray) {

        _subArray = [NSMutableArray new];

    }

    return _subArray;

}

#pragma mark - 单例

+(instancetype)manager{

    staticMQTTManager*manager;

    staticdispatch_once_tonceToken;

    dispatch_once(&onceToken, ^{

        manager = [[MQTTManageralloc]init];

        // 关闭打印日志

        ddLogLevel = DDLogLevelOff;

    });

    returnmanager;

}

#pragma mark -  生成cliendId 的方法

-(NSString*)GetCliendId:(NSString*)userid{

    NSString *cliend_id = [NSString stringWithFormat:@"witcboxapp%@",userid];

    returncliend_id;

}

#pragma mark -  配置MQTT操作

-(void)BindWithCliendId:(NSString*)cliendId SetDelegate:(id)delegate{

    if(delegate!=nil) {

        self.delegate= delegate;

    }

    // 设置初始值

    self.isDiscontent = NO;

    // 客户端 Id,用于区别客户端

    self.cliendId= cliendId;

    // 设置地址和端口号

    MQTTWebsocketTransport *transport = [[MQTTWebsocketTransport alloc] init];

    transport.host = MQTTHost;

    transport.port= MQTTPort;

    // 配置MQTTSession

    self.mySession = [[MQTTSession alloc] init];

    self.mySession.delegate = self;

    self.mySession.transport= transport;

    self.mySession.userName = MQTTUserName;

    self.mySession.password = MQTTPassword;

    self.mySession.clientId= cliendId;

    self.mySession.connectMessage.qos= 2;  // MQTTQosLevelExactlyOnce

    // 断线重连时 如果为yes,会自动订阅回消息,如果为no,则要手动订阅topic,不然会收不到消息

    self.mySession.willRetainFlag = NO;

    // 设置遗言⚠️ 必须设置遗言主题以及msg 不然会报异常

    // 开启遗言

    self.mySession.willFlag = YES;

     // 遗言主题

     self.mySession.willTopic =@"主题内容";

      // 遗言信息

      NSString*willMsgStr    =@"主题信息";

      NSData *data = [willMsgStr dataUsingEncoding:NSUTF8StringEncoding];

      self.mySession.willMsg= data;

    // 如果clean设置为true,则代理将不会存储客户端的任何信息,并将清除之前持续会话的所有信息

    self.mySession.cleanSessionFlag = true;

    // 监听连接状态

    [self.mySession addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

    // 心跳

    self.mySession.keepAliveInterval = 2;

    // 连接服务,并设置超时时间

    [self.mySession connectAndWaitTimeout:1];

}

#pragma mark ---- 【MQTTManager】连接状态改变

- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {

    NSLog(@"当[MQTTManager]与服务器的连接状态发生变化时,会回调此方法返回当前状态%ld",(long)self.mySession.status);

    switch (self.mySession.status) {

        case MQTTSessionStatusClosed:

        {

            NSLog(@"[MQTT连接关闭]");

            if (appDelegate.NetworkStatus != AFNetworkReachabilityStatusNotReachable) {

                // 这个是为了区分是主动断开还是被动断开

                if(!self.isDiscontent){

                    [selfMySessionReConnect];

                }

            }

        }

            break;

        case MQTTSessionStatusConnected:

        {

            NSLog(@"[MQTT连接成功]");

            //  取消延迟

            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(MySessionReConnect) object:nil];

            // 连接成功订阅全局主题以及发送用户上线提醒

            NSString *of_user_id = [[NSUserDefaults standardUserDefaults] objectForKey:@"of_user_id"];

            if(of_user_id!=nil) {

                [self subscribeTopic:[NSString stringWithFormat:IOS_SRT_GlobalTopic,of_user_id]];

            }

        }


            break;

        case MQTTSessionStatusConnecting:

        {

            NSLog(@"[MQTT连接中]");

        }


            break;

        case MQTTSessionStatusError:

            NSLog(@"[MQTT连接错误]");


            break;

        case MQTTSessionStatusDisconnecting:

            NSLog(@"[MQTT正在断开连接]");


        default:

            break;

    }

    if(self.delegate&&[self.delegaterespondsToSelector:@selector(MQTTManagerSessionStatus:)]) {

        // 给使用类返回状态,以便做逻辑处理

        NSLog(@"[MQTT正在执行代理]");

        [self.delegate MQTTManagerSessionStatus:self.mySession.status];

    }

}

#pragma mark - 重新连接

-(void)MySessionReConnect{

    // 连接服务,并设置超时时间

    [self.mySession connectAndWaitTimeout:1];

}

#pragma mark - 订阅主题

- (void)subscribeTopic:(NSString*)topic {

    if (self.mySession.status != MQTTSessionStatusConnected  && ![self.subArray containsObject:topic]) {

        [self.subArrayaddObject:topic];

        return;

    }

    __weak typeof(self) weakSelf = self;


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        dispatch_async(dispatch_get_main_queue(), ^{


            [self.mySession subscribeToTopic:topic atLevel:MQTTQosLevelAtLeastOnce subscribeHandler:^(NSError *error, NSArray *gQoss) {


                if(error) {

                    NSLog(@"MQTT订阅主题 - subscribeTopic failed ----- topic = %@ \n %@",topic,error.localizedDescription);

                    [weakSelf.subArrayaddObject:topic];

                    [weakSelfsubscribeTopicInArray];

                    if(weakSelf.delegate&&[weakSelf.delegaterespondsToSelector:@selector(MQTTManagerSubscribeTopicSuccessOrNot:Topic:)]) {

                        [weakSelf.delegate MQTTManagerSubscribeTopicSuccessOrNot:NO Topic:topic];

                    }

                }else{

                    if([weakSelf.subArraycontainsObject:topic]) {

                        [weakSelf.subArrayremoveObject:topic];

                    }

                    if(weakSelf.delegate&&[weakSelf.delegaterespondsToSelector:@selector(MQTTManagerSubscribeTopicSuccessOrNot:Topic:)]) {

                        [weakSelf.delegate MQTTManagerSubscribeTopicSuccessOrNot:YES Topic:topic];

                    }

                    NSString *of_user_id = [[NSUserDefaults standardUserDefaults] objectForKey:@"of_user_id"];

                    NSString*globalTopic = [NSStringstringWithFormat:IOS_SRT_GlobalTopic,of_user_id];

                    if([topicisEqualToString:globalTopic]) {

                        NSLog(@"[MQTT全局主题订阅成功]");

                        // 开启全局timer发送客户端上线通知给服务器

                        [[MQTTSHLTimer manager] startTimerWithType:carOnlineToServer SendMsgInfo:@{@"sendTopic":[NSString stringWithFormat:IOS_SRT_UserSendTopic,of_user_id]}];

                    }


                    NSLog(@"MQTT订阅主题 - subscribeTopic sucessfull 成功! topic = %@  \n %@",topic,gQoss);

                }

            }];


        });

    });

}

#pragma mark - 订阅主题失败,设置重新订阅

-(void)subscribeTopicInArray{

    for(NSString*topicinself.subArray) {

        [selfsubscribeTopic:topic];

    }

}

#pragma mark - 取消订阅主题

- (void)unsubscribeTopic:(NSString*)topic {

    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        dispatch_async(dispatch_get_main_queue(), ^{


            [self.mySession unsubscribeTopic:topic unsubscribeHandler:^(NSError *error) {

                if(error) {

                    NSLog(@"MQTT取消订阅 - unsubscribeTopic failed ----- topic = %@ \n %@",topic,error.localizedDescription);

                    if(weakSelf.delegate&&[weakSelf.delegaterespondsToSelector:@selector(MQTTManagerCancerSubscribeTopicSuccessOrNot:Topic:)]) {

                        [weakSelf.delegate MQTTManagerCancerSubscribeTopicSuccessOrNot:NO Topic:topic];

                    }

                }else{

                    NSLog(@"MQTT取消订阅 - unsubscribeTopic sucessfull 成功! topic = %@ ",topic);

                    if(weakSelf.delegate&&[weakSelf.delegaterespondsToSelector:@selector(MQTTManagerCancerSubscribeTopicSuccessOrNot:Topic:)]) {

                        [weakSelf.delegate MQTTManagerCancerSubscribeTopicSuccessOrNot:YES Topic:topic];

                    }

                }

            }];

        });

    });

}

#pragma mark - 向对应主题发布消息

// 发送对象

- (void)sendDataToTopic:(NSString*)topic dict:(NSDictionary*)dict {

    [self.mySession publishJson:dict onTopic:topic];

}

- (void)sendDataToTopic:(NSString*)topic SendMsg:(NSString*)msg SendMsgType:(NSString*)type{

    if (self.mySession.status == MQTTSessionStatusConnected) {

        // 记录发送的主题以及消息(用作逻辑判断)

        self.MQTTSendTopic= topic;

        self.MQTTSendMsg  = msg;

        self.MQTTSendMsgType= type;

        NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];

        if(data) {

            [self.mySession publishData:data onTopic:topic retain:false qos:MQTTQosLevelAtLeastOnce];

        }else{

            NSLog(@"[MQTTManager] 发送数据异常");

        }

    }


}

#pragma mark - 数据接收回调MQTTSessionDelegate

- (void)newMessage:(MQTTSession*)session data:(NSData*)data onTopic:(NSString*)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsignedint)mid {

    NSMutableString *string = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(@"thl -----  string = %@ \n -------- topic:%@ --------- \n--------mid:%d--------",string,topic,mid);

    // 收到订阅消息

    if(self.delegate&& [self.delegaterespondsToSelector:@selector(MQTTManagerReceiveMessage:data:onTopic:qos:retained:mid:)]) {

        [self.delegate MQTTManagerReceiveMessage:session data:data onTopic:topic qos:qos retained:retained mid:mid];

    }


}

#pragma mark - 主动断开连接

- (void)disconnect {

    self.isDiscontent=YES;    // 断开连接

    // 置空操作

    self.delegate=nil;

    // 断开连接

    [self.mySession disconnect];

    // 置空

    self.mySession = nil;

    // 移除状态监听

    [self.mySession removeObserver:self forKeyPath:@"status"];

}

@end

你可能感兴趣的:(iOS MQTT(MQTTClient) 车联网之实战篇)