开发中我们经常使用到长连接,需要时时获取到最新数据状态,常用的时时获取最新状态的方法用很多,但是开发中一般都会使用socket长连接。针对这个也有一个库CocoaAsyncSocket,对socket也已经封装的很好,我们也只需要下载对应的库就可以了,下载链接:
https://github.com/robbiehanson/CocoaAsyncSocket
今天介绍一下MQTT长连接。cocoapods 的使用方法很简单,pod 'MQTTClient' 就可以了,那非cocoapods 到底需要导入那些库呢?
csdn连接
https://download.csdn.net/download/wangqinglei0307/10824049
github连接
https://github.com/ckrey/MQTT-Client-Framework
点击此链接,既可以下载到的MQTT库然后直接将库导入到工程就可以了。
写工程的时候,需要使用ca.crt证书,这个是需要我们去生成的,后台开发人员,都会讲这个文件直接给我们,我们只需要将这个文件直接拽进工程就可以了
还有一个就是MQTT要用到一个topic(字符串类型),这个是一个很重要,需要和服务器统一,我们要靠这个订阅,简单来说就是,我们发送消息和接收消息,可能类型是不同的,都是通过这个topic来决定,你发给第一个topic消息,服务器只会返回第一个topic的消息反馈,这样可以有效管理不同的信息处理
然后就是使用了,首先为了方便使用。我们写一个MQTT 管理类 MQTTManager.h文件
粘贴代码:
MQTTManager.h
#import <Foundation/Foundation.h>
@interface MQTTManager : NSObject
/**
创建单例,返回始终为一个对象
@return self
*/
+ (MQTTManager *)sharedManager;
#pragma mark - MQTT连接
- (void)userMQTTBind;
#pragma mark - 订阅
- (void)subscribeTopic:(NSString *)topic;
#pragma mark - 发布消息
- (void)sendDataToTopic:(NSString *)topic dict:(NSDictionary *)dict;
#pragma mark - 主动断开
- (void)disconnect;
#pragma mark - 取消订阅
- (void)unsubscribeTopic:(NSString *)topic;
MQTTManager.m文件
复制代码:
#import "MQTTManager.h"
// 导入头文件
#import "MQTTClient.h"
@interface MQTTManager ()<MQTTSessionManagerDelegate,MQTTSessionDelegate>
@property (nonatomic, strong) MQTTSessionManager *sessionManager;
@property (nonatomic, strong) MQTTSession *mySession;
@property (nonatomic, strong) NSMutableArray *subArray;
@property (nonatomic, assign) BOOL isDiscontent;
@end
@implementation MQTTManager
/**
创建单例,返回始终为一个对象
@return self
*/
+ (MQTTManager *)sharedManager{
static MQTTManager *manager = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
manager = [[self alloc] init];
});
return manager;
}
- (MQTTSessionManager*)sessionManager{
if (_sessionManager == nil) {
_sessionManager = [[MQTTSessionManager alloc]init];
_sessionManager.delegate = self;
}
return _sessionManager;
}
- (NSMutableArray*)subArray{
if ( _subArray == nil ) {
_subArray = [[NSMutableArray alloc]init];
}
return _subArray;
}
#pragma mark - 绑定
- (void)userMQTTBind{
self.mySession = [[MQTTSession alloc]initWithClientId:[NSString stringWithFormat:@"%@@@@%@%@",@"GID_XXX",@"",[NSObject getUserDeviceUUIDString]]
userName:@"用户名"
password:@"密码"
keepAlive:60
cleanSession:YES
will:NO
willTopic:nil
willMsg:nil
willQoS:MQTTQosLevelAtLeastOnce
willRetainFlag:NO
protocolLevel:4 // 这块需要注意,代表mqtt版本等级,需要和后台确认一下,否则会出现因为版本等级而导致mqtt无法使用(本人亲自踩过坑)
runLoop:[NSRunLoop mainRunLoop]
forMode:nil
securityPolicy:[self customSecurityPolicy]];
self.isDiscontent = NO;
self.mySession.delegate = self;
[self.mySession connectToHost:@"连接的服务器域名或者IP" port:域名 usingSSL:YES];
[self.mySession addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
switch (self.mySession.status) {
case MQTTSessionStatusClosed:
NSLog(@"连接关闭");
if (!self.isDiscontent) [self.mySession connect]; //这个是为了区分是主动断开还是被动断开。
break;
case MQTTSessionStatusConnected:
NSLog(@"连接成功");
break;
case MQTTSessionStatusConnecting:
NSLog(@"连接中");
break;
case MQTTSessionStatusError:
NSLog(@"连接错误");
break;
case MQTTSessionStatusDisconnecting:
NSLog(@"正在断开连接");
default:
break;
}
}
- (MQTTSSLSecurityPolicy *)customSecurityPolicy{
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"crt"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
MQTTSSLSecurityPolicy *securityPolicy = [MQTTSSLSecurityPolicy policyWithPinningMode:MQTTSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesCertificateChain = YES;
securityPolicy.validatesDomainName = NO;
securityPolicy.pinnedCertificates = @[certData];
return securityPolicy;
}
#pragma mark - 订阅
- (void)subscribeTopic:(NSString *)topic {
if (self.mySession.status != MQTTSessionStatusConnected && ![self.subArray containsObject:topic]) {
[self.subArray addObject:topic];
return;
}
__weak typeof(self) weakSelf = self;
self.sessionManager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:MQTTQosLevelExactlyOnce] forKey:topic];
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<NSNumber *> *gQoss) {
if (error) {
NSLog(@"subscribeTopic failed ----- topic = %@ \n %@",topic,error.localizedDescription);
[weakSelf.subArray addObject:topic];
} else {
if ([weakSelf.subArray containsObject:topic]) {
[weakSelf.subArray removeObject:topic];
}
NSLog(@"subscribeTopic sucessfull 成功! topic = %@ \n %@",topic,gQoss);
}
}];
});
});
}
#pragma mark - 取消订阅
- (void)unsubscribeTopic:(NSString *)topic {
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(@"unsubscribeTopic failed ----- topic = %@ \n %@",topic,error.localizedDescription);
} else {
NSLog(@"unsubscribeTopic sucessfull 成功! topic = %@ ",topic);
}
}];
});
});
}
#pragma mark - 发布消息
- (void)sendDataToTopic:(NSString *)topic dict:(NSDictionary *)dict {
[self.mySession publishJson:dict onTopic:topic];
}
#pragma mark - MQTTSessionDelegate 获取返回内容,与后台协商
- (void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid {
NSMutableString *string = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"thl ----- data = %@ \n thl ----- str = %@ \n -------- topic:%@ --------- \n",data,string,topic);
}
- (void)session:(MQTTSession*)session newMessage:(NSData*)data onTopic:(NSString*)topic{
}
然后根据我们的需要直接使用就可以了。例如:
粘贴代码:
//建立MQTT连接
[[MQTTManager sharedManager]userMQTTBind];
//连接以后发送订阅,订阅名字 (我是第一订阅)
[[MQTTManager sharedManager]subscribeTopic:@"我是第一订阅"];
// 发送订阅信息 (@"我是第一订阅")下的 信息为:@{@"key":@"所传的参数"},信息
[[MQTTManager sharedManager]sendDataToTopic:@"我是第一订阅" dict:@{@"key":@"所传的参数"}];