iOS中MQTT和WS的简单混合使用
历经三天的寻找和尝试,终于完成了一个任务,任务要求的服务器访问用WebSocket的URL格式的,而访问结束后要进行订阅和消息发送,这更多出现在MQTT协议的框架里,例如这个文章
https://www.jianshu.com/p/80ea4507ca74 《iOS MQTT----MQTTClient实战-看这篇的就够了》。但是这个文章的框架访问示例代码的host和端口port设置的方式如下:
-(void)loginMQTT{/*设置ip和端口号*/self.transport.host=_ip;self.transport.port=_port;/*设置MQTT账号和密码*/self.mqttSession.transport=self.transport;//给MQTTSession对象设置基本信息self.mqttSession.delegate=self;//设置代理[self.mqttSession setUserName:_userName];[self.mqttSession setPassword:_password];//会话链接并设置超时时间[self.mqttSession connectAndWaitTimeout:5];
}
该方式的host格式严格是“xxx.xxx.xx.xx”,如果格式错误,会直接访问报错,域名解析错误。报错信息如下:
2021-05-27 09:49:58.630527+0800 Intelligent_fire_protection[28024:9675835] Error Domain=kCFErrorDomainCFNetwork Code=2 "(null)" UserInfo={_kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, kCFGetAddrInfoFailureKey=8}
有时也会返回404,报错信息如下
Error Domain=SRWebSocketErrorDomain Code=2132 "received bad response code from server 404" UserInfo={NSLocalizedDescription=received bad response code from server 404, HTTPResponseStatusCode=404}
如果格式包含了path,例如“ws://xxxx/xxxx.xx.xx/mqtt”,需要将 self.transport.host=_ip;和self.transport.port=_port;删去,改成self.transport.url=“ws://xxxx/xxxx.xx.xx/mqtt”。
实现效果我没具体实践,应该不会有问题,起初我没发现这个url属性,host地址又被框架定死没办法和后台统一,访问又一直失败,于是我换了一个网上的demo,在另一个demo里发现了url属性,并完成了我的需求,后来回头查看这个框架的时候,发现也有url属性。爬坑了。
接下来我简单介绍一下我应用的这个框架:
该框架引用来自:https://blog.csdn.net/sinat_23907467/article/details/84840699,《iOS MQTT 简单使用流程》
配置MQTT。 MQTTClient 配置更多 是可持续更新,可配置 SSL
pod 'MQTTClient'
pod 'MQTTClient/MinL'
pod 'MQTTClient/ManagerL'
pod 'MQTTClient/WebsocketL'
.h文件中添加文件头和MQTTsession变量
#import <MQTTClient.h>
#import <MQTTWebsocketTransport.h>
#define WEAKSELF __typeof(&*self) __weak weakSelf = self;@property (nonatomic, strong) MQTTSession *mySession;
.m文件中添加协议<MQTTSessionDelegate,MQTTSessionManagerDelegate>,定义相应的变量,本文后续代码都在改.m文件中添加
@property(nonatomic,strong)NSString *server_ip;
@property(nonatomic,strong)NSString *userName;
@property(nonatomic,strong)NSString *passWord;
@property(nonatomic,strong)NSArray *topics;
文章中用到了下面的代码,但我在应用时候链接服务器报错了,我便把这部分代码注释掉了,实际上这部分代码只是避免持续等待连接卡死的情况,当连接时间超过某一个时间长度就强制断开申请连接。后文中我另加了一行代码实现这个功能。
MQTTCFSocketTransport *transport = [[MQTTCFSocketTransport alloc] init];transport.host = @"localhost";transport.port = 1883;MQTTSession *session = [[MQTTSession alloc] init];session.transport = transport;session.delegate = self;[session connectAndWaitTimeout:30];
WebSocket的初始化和链接代码:
引用的文章中用的是host和port,但这个不符合web的地址格式,往往web会有一个path路径,所以访问会报错,错误代码是5,MQTTSessionEventConnectionClosedByBroker。所以还需要在代码中添加一行transport.path=@"ws";或者将post和port两行注释掉,直接手动输入一个url 即 NSURL *url=[NSURL URLWithString:_server_ip]; transport.url=url; 其中_server_ip的格式为“ws://xxxx.xxxx.xx.xx/ws”
另外,上文中我注释掉的代码在此处实现了,即 [session connectAndWaitTimeout:10];
引用的文章中有个 [self reconnect];代码,实际是重新连接功能,实际使用时候文章没提供,已经没有这个函数了,所以我用的[self.mySession connect];代替。
WEAKSELF
// NSString *myId=[NSString stringWithFormat:@"%d",arc4random()%10000];NSString *clientID =[NSString stringWithFormat:@"%@|iOS|%@",[[NSBundle mainBundle] bundleIdentifier],[UIDevice currentDevice].identifierForVendor.UUIDString];;MQTTWebsocketTransport *transport = [[MQTTWebsocketTransport alloc] init];
// transport.host = [NSString stringWithFormat:@"%@",server_ip];
// transport.port = 9999; // 端口号NSURL *url=[NSURL URLWithString:_server_ip];transport.url=url;
// transport.path=@"ws";transport.tls = YES; // 根据需要配置 YES 开起 SSL 验证 此处为单向验证 双向验证 根据SDK 提供方法直接添加MQTTSession *session = [[MQTTSession alloc] init];NSString *linkUserName = _userName;NSString *linkPassWord = _passWord;[session setUserName:linkUserName];[session setClientId:clientID];[session setPassword:linkPassWord];[session setKeepAliveInterval:5];session.transport = transport;session.delegate = self;[session connectAndWaitTimeout:10];self.mySession = session;[self.mySession connect];//self reconnect[self.mySession addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; //添加事件监听
WebSocket 监听,响应事件:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {switch (self.mySession.status) {case MQTTSessionManagerStateClosed:NSLog(@"连接已经关闭");break;case MQTTSessionManagerStateClosing:NSLog(@"连接正在关闭");break;case MQTTSessionManagerStateConnected:NSLog(@"已经连接");break;case MQTTSessionManagerStateConnecting:NSLog(@"正在连接中");break;case MQTTSessionManagerStateError: {// NSString *errorCode = self.mySession.lastErrorCode.localizedDescription;NSString *errorCode = self.mySession.description;NSLog(@"连接异常 ----- %@",errorCode);}break;case MQTTSessionManagerStateStarting:NSLog(@"开始连接");break;default:break;}
}
session Delegate 协议
连接 返回状态
根据我的项目需求,我直接在订阅成功后直接对topic进行了订阅,即代码中for循环,读者可以自行更改订阅逻辑。
-(void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error{if (eventCode == MQTTSessionEventConnected) {NSLog(@"链接MQTT 成功");// 方法 封装 可外部调用for (NSString *topic in _topics) {[session subscribeToTopic:topic atLevel:2 subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss){if (error) {NSLog(@"Subscription failed %@", error.localizedDescription);} else {NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);// [self send];}}];}// this is part of the block API}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{//MQTTSessionEventConnectionClosedByBrokerNSLog(@"MQTT链接 其他错误");}if (error) {NSLog(@"链接报错 -- %@",error);}
}
收到来自服务器的信息,并对改信息进行相应处理
-(void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid
{NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
// NSLog(@"EasyMqttService mqtt connect success %@",dic);// 做相对应的操作
}
取消订阅,断开与服务器的链接:
-(void)closeMQTTClient{[self.mySession disconnect];[self.mySession unsubscribeTopics:@[@"已经订阅的主题"] unsubscribeHandler:^(NSError *error) {if (error) {NSLog(@"取消订阅失败");}else{NSLog(@"取消订阅成功");}}];
}
以下内容是我封装了该框架,并留了三个方法用于链接,发送数据和断开连接。读者自行在.h文件中添加对应的方法:
-(void)connectServer:(NSString *)urlString userName:(NSString *)userName passWord:(NSString *)passWord topic:(NSArray *)topics{_server_ip=urlString;_userName=userName;_passWord=passWord;_topics=topics;[self websocket];}
-(void)disConnectServer{[_mySession closeAndWait:1];self.mySession.delegate=nil;//代理_mySession=nil;
// _transport=nil;//连接服务器属性_server_ip=nil;//服务器ip地址
// _port=0;//服务器ip地址_userName=nil;//用户名_passWord=nil;//密码
// _topic=nil;//单个主题订阅_topics=nil;//多个主题订阅
}
-(void)sendMassage:(NSData *)msg topic:(NSString *)topic{[self.mySession publishData:msg onTopic:topic retain:NO qos:MQTTQosLevelExactlyOnce publishHandler:^(NSError *error) {if (error) {NSLog(@"发送失败 - %@",error);}else{NSLog(@"发送成功");}}];
}
iOS中MQTT和WS的简单混合使用相关推荐
- ios android md5加密,iOS中使用MD5加密
在iOS中使用MD5加密较简单,需要引入头文件CommonCrypto/CommonDigest.h,我们单独新建一个用于MD5加密的类Encryption,此类继承NSObject. Encrypt ...
- iOS中SDK的简单封装与使用
一.功能总述 在博客开始的第一部分,我们先来看一下我们最终要实现的效果.下图中所表述的就是我们今天博客中要做的事情,下方的App One和App Two都植入了我们将要封装的LoginSDK, 两个A ...
- 1、OPenGL ES - 简介、iOS中GLKit简单应用
OPenGL ES - 简介.iOS中GLKit简单应用 一.OPenGL ES 1.简介: OpenGL ES 是以手持和嵌入式为目标的高级的3D图形应用程序编程接口(API),OpenGL ES ...
- iphone smtp服务器没有响应,电子邮件卡在iPhone或iPad上的发件箱?如何修复iOS中的未发送邮件 | MOS86...
您曾经在iOS中发送电子邮件,只能将信息卡在iPhone,iPad或iPod touch的邮件应用发件箱中?你知道这是什么时候发生的,因为在iOS的Mail应用程序的底部,状态栏在iOS中显示1个未发 ...
- iOS中几种数据持久化方案总结
概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) ...
- iOS中关于NSTimer使用知多少
看到这个标题,你可能会想NSTimer不就是计时器吗,谁不会用,不就是一个能够定时的完成任务的东西吗? 我想说你知道NSTimer会retain你添加调用方法的对象吗?你知道NSTimer是要加到ru ...
- 在iOS中使用tableView
为什么80%的码农都做不了架构师?>>> UITableView是iOS中最常用的控件了,所以使用起来也很简单. ViewContoller.h 文件 (继承UITableVi ...
- 关于ios中编译ffmpeg0.9.2库
很多朋友在问如何在ios中编译ffmpeg库,虽说网上的教程很多,但是大部分都说按其操作,最后编译总是不成功,正好我最近的项目要用到ffmpeg,所以就再次编译了,同时在这里记下,方便需要参考的朋友. ...
- iOS 中KVC、KVO、NSNotification、delegate 总结及区别
iOS 中KVC.KVO.NSNotification.delegate 总结及区别 1.KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属 ...
最新文章
- 【特征工程】与【表示学习】
- 设计模式(中介者模式)
- oracle数据库中表被锁,Oracle数据库表被锁问题处理
- storm的流分组策略
- Go语言vscode环境配置
- linux free 命令中buffers、cached以及-/+ buffers/cache解析
- 服务器文件同步本站,服务器文件同步
- Jmeter --- Http Cookie Manager
- 系统性谈谈软件可靠性——第5讲:软件测试及常见测试用例设计方法
- 计算机的科学导论pdf,教材计算机科学导论.PDF
- 论文参考文献正确插入方法 (一)
- ifcfg-eth0配置详解
- 徐思201771010132《面向对象程序设计(java)》第十六周学习总结
- 儿童过敏性鼻炎的最佳治疗方法
- keras中sample_weight的使用
- 【CSDN|每日一练】小艺的英文名
- Unknown host 'dl.google.com'. You may need to adjust the proxy settings in Gradle.
- 海康IPC+ffmpeg+nginx+ckplayer实现网页实时预览监控视频
- web调用摄像头拍照并上传到服务器
- Java throw和throws关键字的使用及区别