├── README.md ├── RHMQTTKit ├── RHMQTT.m ├── RHMQTT.h ├── RHMQTTDecoder.h ├── RHMQTTEncoder.h ├── RHMQTTEncoder.m ├── RHMQTTClient.h ├── RHMQTTDecoder.m ├── RHMQTTPacket.h ├── RHMQTTClient.m └── RHMQTTPacket.m ├── RHMQTTKitDemo ├── Podfile ├── RHMQTTKitDemo.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── RHMQTTKitDemo │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── AppDelegate.m │ └── ViewController.m ├── RHMQTTKitDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Podfile.lock ├── .gitignore ├── RHMQTTKit.podspec └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # RHMQTTKit 2 | 基于RHSocketKit框架实现的MQTT协议,底层使用CocoaAsyncSocket。 3 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTT.m: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTT.m 3 | // RHSocketKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/11. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | //#import "RHMQTT.h" 10 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform:ios,'9.0' 2 | 3 | target 'RHMQTTKitDemo' do 4 | pod 'RHSocketKit/Extend', :git=> 'https://github.com/zhu410289616/RHSocketKit.git', :tag => '2.3.6' 5 | pod 'RHMQTTKit', :path => '../RHMQTTKit.podspec' 6 | end 7 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTT.h: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTT.h 3 | // RHSocketKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/11. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import "RHMQTTPacket.h" 10 | #import "RHMQTTEncoder.h" 11 | #import "RHMQTTDecoder.h" 12 | #import "RHMQTTClient.h" 13 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/16. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTDecoder.h 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 16/2/23. 6 | // Copyright © 2016年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RHMQTTDecoder : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTEncoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTEncoder.h 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 16/2/23. 6 | // Copyright © 2016年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RHMQTTEncoder : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/16. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/16. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | -------------------------------------------------------------------------------- /RHMQTTKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | 3 | spec.name = "RHMQTTKit" 4 | spec.version = "0.0.1" 5 | spec.summary = "MQTT 通信代码" 6 | 7 | spec.description = <<-DESC 8 | 基于RHSocketKit框架实现的MQTT协议,底层使用CocoaAsyncSocket。 9 | DESC 10 | 11 | spec.homepage = "https://github.com/zhu410289616/RHMQTTKit" 12 | spec.license = { :type => "MIT", :file => "LICENSE" } 13 | 14 | spec.author = { "zhu410289616" => "zhu410289616@163.com" } 15 | spec.platform = :ios, "9.0" 16 | spec.source = { :git => "https://github.com/zhu410289616/RHMQTTKit.git", :tag => "#{spec.version}" } 17 | 18 | spec.subspec "RHMQTTKit" do |cs| 19 | cs.source_files = "RHMQTTKit/**/*" 20 | cs.dependency "RHSocketKit" 21 | cs.dependency "CocoaAsyncSocket" 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTEncoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTEncoder.m 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 16/2/23. 6 | // Copyright © 2016年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import "RHMQTTEncoder.h" 10 | #import "RHMQTTPacket.h" 11 | #import "RHSocketUtils.h" 12 | 13 | @implementation RHMQTTEncoder 14 | 15 | - (void)encode:(id)upstreamPacket output:(id)output 16 | { 17 | RHMQTTPacket *packet = (RHMQTTPacket *)upstreamPacket; 18 | NSData *data = [packet data]; 19 | NSMutableData *sendData = [NSMutableData dataWithData:data]; 20 | 21 | NSTimeInterval timeout = [upstreamPacket timeout]; 22 | 23 | RHSocketLog(@"[encode] timeout: %f, data: %@", timeout, [RHSocketUtils hexStringFromData:sendData]); 24 | [output didEncode:sendData timeout:timeout]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTClient.h 3 | // Pods 4 | // 5 | // Created by ruhong zhu on 2021/8/21. 6 | // 7 | 8 | #import 9 | #import 10 | #import "RHMQTTPacket.h" 11 | 12 | @class RHMQTTClient; 13 | 14 | @protocol RHMQTTClientDelegate 15 | 16 | - (void)mqttConnected:(RHMQTTClient *)mqtt host:(NSString *)host port:(int)port; 17 | - (void)mqttDisconnect:(RHMQTTClient *)mqtt error:(NSError *)error; 18 | - (void)mqtt:(RHMQTTClient *)mqtt received:(RHMQTTPacket *)packet; 19 | 20 | - (void)mqtt:(RHMQTTClient *)mqtt publish:(RHMQTTPublish *)publish; 21 | 22 | @end 23 | 24 | @interface RHMQTTClient : NSObject 25 | 26 | @property (nonatomic, weak) id delegate; 27 | 28 | - (void)startWithHost:(NSString *)host port:(int)port; 29 | - (void)stop; 30 | 31 | - (void)asyncSendPacket:(RHMQTTPacket *)packet; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 朱如洪 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - CocoaAsyncSocket (7.6.5) 3 | - RHMQTTKit (0.0.1): 4 | - RHMQTTKit/RHMQTTKit (= 0.0.1) 5 | - RHMQTTKit/RHMQTTKit (0.0.1): 6 | - CocoaAsyncSocket 7 | - RHSocketKit 8 | - RHSocketKit (2.3.0): 9 | - RHSocketKit/Core (= 2.3.0) 10 | - RHSocketKit/Core (2.3.0): 11 | - CocoaAsyncSocket 12 | - RHSocketKit/Extend (2.3.0): 13 | - RHSocketKit/Core 14 | 15 | DEPENDENCIES: 16 | - RHMQTTKit (from `../RHMQTTKit.podspec`) 17 | - RHSocketKit/Extend (from `https://github.com/zhu410289616/RHSocketKit.git`, tag `2.3.6`) 18 | 19 | SPEC REPOS: 20 | trunk: 21 | - CocoaAsyncSocket 22 | 23 | EXTERNAL SOURCES: 24 | RHMQTTKit: 25 | :path: "../RHMQTTKit.podspec" 26 | RHSocketKit: 27 | :git: https://github.com/zhu410289616/RHSocketKit.git 28 | :tag: 2.3.6 29 | 30 | CHECKOUT OPTIONS: 31 | RHSocketKit: 32 | :git: https://github.com/zhu410289616/RHSocketKit.git 33 | :tag: 2.3.6 34 | 35 | SPEC CHECKSUMS: 36 | CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 37 | RHMQTTKit: c563288b92a4dbeade92781987b1bcf5a33c0a6b 38 | RHSocketKit: f25606cece9e695cd3fc22a04d8308b1e2f9e572 39 | 40 | PODFILE CHECKSUM: 22677b35fcdf45f7e992999eb1eae61595c0a176 41 | 42 | COCOAPODS: 1.9.1 43 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/16. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTDecoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTDecoder.m 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 16/2/23. 6 | // Copyright © 2016年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import "RHMQTTDecoder.h" 10 | #import "RHMQTTPacket.h" 11 | #import "RHSocketUtils.h" 12 | 13 | @implementation RHMQTTDecoder 14 | 15 | - (NSInteger)decode:(id)downstreamPacket output:(id)output 16 | { 17 | NSData *downstreamData = [downstreamPacket object]; 18 | NSInteger headIndex = 0; 19 | NSInteger lengthMultiplier = 1; 20 | //先读区2个字节的协议长度 (前2个字节为数据包的固定长度) 21 | while (downstreamData && (downstreamData.length - headIndex) >= (1 + lengthMultiplier)) { 22 | NSData *lenData = [downstreamData subdataWithRange:NSMakeRange(headIndex, 1 + lengthMultiplier)]; 23 | 24 | //剩余长度remainingLength(1-4个字节,可变) 25 | //可变头部内容字节长度 + Playload/负荷字节长度 = 剩余长度 26 | NSInteger remainingLength = 0; 27 | int8_t digit = 0; 28 | [lenData getBytes:&digit range:NSMakeRange(lengthMultiplier, 1)]; 29 | 30 | if ((digit & 0x80) == 0x00) { 31 | //已经读区到剩余长度,可以解码 32 | remainingLength += (digit & 0x7f); 33 | } else { 34 | lengthMultiplier++; 35 | remainingLength += (digit & 0x7f); 36 | NSAssert(lengthMultiplier <= 4, @"remain length error ..."); 37 | continue; 38 | } 39 | 40 | if (downstreamData.length - headIndex < remainingLength + 1 + lengthMultiplier) { 41 | break; 42 | } 43 | NSData *frameData = [downstreamData subdataWithRange:NSMakeRange(headIndex, remainingLength + 1 + lengthMultiplier)]; 44 | RHSocketLog(@"[decode] frame data: %@", [RHSocketUtils hexStringFromData:frameData]); 45 | 46 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:frameData]; 47 | int8_t fixedHeaderData = [byteBuffer readInt8:0]; 48 | RHMQTTFixedHeader *fixedHeader = [[RHMQTTFixedHeader alloc] initWithByte:fixedHeaderData]; 49 | 50 | NSData *remainingLengthData = [byteBuffer readData:1+lengthMultiplier length:remainingLength]; 51 | 52 | // RHMQTTVariableHeader *variableHeader = nil; 53 | // if (remainingLength - 1 - lengthMultiplier > 0) { 54 | // NSData *variableHeaderData = [byteBuffer readData:1+lengthMultiplier length:remainingLength]; 55 | // variableHeader = [[RHMQTTVariableHeader alloc] initWithObject:variableHeaderData]; 56 | // } 57 | 58 | RHMQTTPacket *packet = [[RHMQTTPacket alloc] initWithObject:frameData]; 59 | packet.remainingLengthData = remainingLengthData; 60 | packet.remainingLength = remainingLength; 61 | packet.fixedHeader = fixedHeader; 62 | // packet.variableHeader = variableHeader; 63 | [output didDecode:packet]; 64 | 65 | headIndex += remainingLength + 2; 66 | lengthMultiplier = 1; 67 | } 68 | return headIndex; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTPacket.h: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTPacket.h 3 | // Pods 4 | // 5 | // Created by ruhong zhu on 2021/8/21. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | //http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html 12 | //http://www.blogjava.net/yongboy/archive/2014/02/09/409630.html 13 | //http://www.blogjava.net/yongboy/archive/2014/02/10/409689.html 14 | 15 | /** message type */ 16 | typedef NS_ENUM(UInt8, RHMQTTMessageType) { 17 | RHMQTTMessageTypeConnect = 1, //client request to connect to server 18 | RHMQTTMessageTypeConnAck = 2, //connect acknowledgment 19 | RHMQTTMessageTypePublish = 3, //publish message 20 | RHMQTTMessageTypePubAck = 4, //publish acknowledgment 21 | RHMQTTMessageTypePubRec = 5, //publish received (assured delivery part 1) 22 | RHMQTTMessageTypePubRel = 6, //publish release (assured delivery part 2) 23 | RHMQTTMessageTypePubComp = 7, //publish complete (assured delivery part 3) 24 | RHMQTTMessageTypeSubscribe = 8, //client subscribe request 25 | RHMQTTMessageTypeSubAck = 9, //subscribe acknowledgment 26 | RHMQTTMessageTypeUnsubscribe = 10, //client unsubscribe request 27 | RHMQTTMessageTypeUnsubAck = 11, //unsubscribe acknowledgment 28 | RHMQTTMessageTypePingReq = 12, //ping request 29 | RHMQTTMessageTypePingResp = 13, //ping response 30 | RHMQTTMessageTypeDisconnect = 14, //client is disconnecting 31 | RHMQTTMessageTypeReserved = 15 //reserved 32 | }; 33 | 34 | /** QoS(Quality of Service,服务质量) */ 35 | typedef NS_ENUM(UInt8, RHMQTTQosLevel) { 36 | RHMQTTQosLevelAtMostOnce = 0, //至多一次,发完即丢弃,<=1 37 | RHMQTTQosLevelAtLeastOnce = 1, //至少一次,需要确认回复,>=1 38 | RHMQTTQosLevelExactlyOnce = 2, //只有一次,需要确认回复,=1 39 | RHMQTTQosLevelReserved = 3 //待用,保留位置 40 | }; 41 | 42 | // ------------------------------------------ 43 | 44 | #pragma mark - RHMQTTFixedHeader 45 | 46 | /** Fixed header/固定头部 */ 47 | @interface RHMQTTFixedHeader : NSObject 48 | 49 | @property (nonatomic, assign) RHMQTTMessageType type; //4bit 50 | @property (nonatomic, assign) BOOL dupFlag; //1bit 51 | @property (nonatomic, assign) RHMQTTQosLevel qos; //2bit 52 | @property (nonatomic, assign) BOOL retainFlag; //1bit 53 | 54 | - (instancetype)initWithByte:(UInt8)byte; 55 | 56 | @end 57 | 58 | // ------------------------------------------ 59 | 60 | #pragma mark - RHMQTTVariableHeader 61 | 62 | /** Variable header/可变头部 */ 63 | @interface RHMQTTVariableHeader : NSObject 64 | 65 | /** protocol name */ 66 | @property (nonatomic, strong) NSString *name; 67 | 68 | /** protocol version number, 1byte */ 69 | @property (nonatomic, assign) UInt8 version; 70 | 71 | /** 72 | * connect flags, 1byte 73 | * 74 | * user name flag: 1bit 75 | * password flag: 1bit 76 | * will retain: 1bit 77 | * will QoS: 2bit 78 | * will flag: 1bit 79 | * clean session: 1bit 80 | * reserved: 1bit 81 | */ 82 | @property (nonatomic, assign) UInt8 connectFlags; 83 | 84 | /** keep alive timer, 2byte */ 85 | @property (nonatomic, assign) UInt16 keepAlive; 86 | 87 | /** publish */ 88 | @property (nonatomic, strong) NSString *topic; 89 | 90 | /** subscribe, publish */ 91 | @property (nonatomic, assign) UInt16 messageId; 92 | 93 | @end 94 | 95 | // ------------------------------------------ 96 | 97 | #pragma mark - RHMQTTPayload 98 | 99 | @interface RHMQTTTopic : NSObject 100 | 101 | /* 102 | * Will Topic 103 | * 104 | * Will Flag值为1,这里便是Will Topic的内容。 105 | * QoS级别通过Will QoS字段定义,RETAIN值通过Will RETAIN标识,都定义在可变头里面。 106 | */ 107 | @property (nonatomic, strong) NSString *topic; 108 | @property (nonatomic, assign) RHMQTTQosLevel qos; 109 | 110 | @end 111 | 112 | // ------------------------------------------ 113 | 114 | #pragma mark - RHMQTTPayload 115 | 116 | /** Payload/消息体 */ 117 | @interface RHMQTTPayload : NSObject 118 | 119 | /* 120 | * Client Identifier(客户端ID) 必填项。 121 | * 122 | * 1-23个字符长度,客户端到服务器的全局唯一标志 123 | * 如果客户端ID超出23个字符长度,服务器需要返回码为2,标识符被拒绝响应的CONNACK消息。 124 | * 125 | * 处理QoS级别1和2的消息ID中,可以使用到。 126 | */ 127 | @property (nonatomic, strong) NSString *clientId; 128 | 129 | /** see RHMQTTTopic */ 130 | @property (nonatomic, strong) NSArray *topics; 131 | 132 | /* 133 | * Will Message (长度有可能为0) 134 | * 135 | * Will Flag若设为1,这里便是Will Message定义消息的内容,对应的主题为Will Topic。 136 | * 如果客户端意外的断开触发服务器PUBLISH此消息。 137 | * 在CONNECT消息中的Will Message是UTF-8编码的,当被服务器发布时则作为二进制的消息体。 138 | */ 139 | @property (nonatomic, strong) NSData *message; 140 | 141 | /** 如果设置User Name标识,可以在此读取用户名称。一般可用于身份验证。协议建议用户名为不多于12个字符,不是必须。*/ 142 | @property (nonatomic, strong) NSString *username; 143 | 144 | /** 如果设置Password标识,便可读取用户密码。建议密码为12个字符或者更少,但不是必须。*/ 145 | @property (nonatomic, strong) NSString *password; 146 | 147 | @end 148 | 149 | // ------------------------------------------ 150 | 151 | #pragma mark - RHMQTTPacket 152 | 153 | @interface RHMQTTPacket : RHSocketPacketRequest 154 | 155 | /** 剩余长度 remainingLength 的二进制数据(1-4个字节,可变) */ 156 | @property (nonatomic, strong) NSData *remainingLengthData; 157 | /** 剩余长度 = 可变头部内容字节长度 + Playload/负荷字节长度 */ 158 | @property (nonatomic, assign) NSInteger remainingLength; 159 | 160 | /** 1 个字节 */ 161 | @property (nonatomic, strong) RHMQTTFixedHeader *fixedHeader; 162 | @property (nonatomic, strong) RHMQTTVariableHeader *variableHeader; 163 | @property (nonatomic, strong) RHMQTTPayload *payload; 164 | 165 | - (NSData *)dataWithFixedHeader; 166 | - (NSData *)dataWithVariableHeader; 167 | - (NSData *)dataWithPayload; 168 | - (NSData *)data; 169 | 170 | @end 171 | 172 | // ------------------------------------------ 173 | 174 | #pragma mark - RHMQTTConnect 175 | 176 | @interface RHMQTTConnect : RHMQTTPacket 177 | 178 | + (RHMQTTConnect *)connectWithClientId:(NSString *)clientId username:(NSString *)username password:(NSString *)password keepAlive:(UInt16)keepAlive cleanSession:(BOOL)cleanSession; 179 | 180 | @end 181 | 182 | // ------------------------------------------ 183 | 184 | #pragma mark - RHMQTTPublish 185 | 186 | @interface RHMQTTPublish : RHMQTTPacket 187 | 188 | @end 189 | 190 | #pragma mark - RHMQTTPublishAck 191 | 192 | @interface RHMQTTPublishAck : RHMQTTPacket 193 | 194 | - (instancetype)initWithMessageId:(int16_t)msgId; 195 | 196 | @end 197 | 198 | #pragma mark - RHMQTTPublishRec 199 | 200 | @interface RHMQTTPublishRec : RHMQTTPacket 201 | 202 | - (instancetype)initWithMessageId:(int16_t)msgId; 203 | 204 | @end 205 | 206 | #pragma mark - RHMQTTPublishRel 207 | 208 | @interface RHMQTTPublishRel : RHMQTTPacket 209 | 210 | - (instancetype)initWithMessageId:(int16_t)msgId; 211 | 212 | @end 213 | 214 | #pragma mark - RHMQTTPublishComp 215 | 216 | /** 217 | 作为QoS level = 2消息流第四个,也是最后一个消息,由收到PUBREL的一方向另一方做出的响应消息。 218 | 完整的消息一览,和PUBREL一致,除了消息类型。 219 | */ 220 | @interface RHMQTTPublishComp : RHMQTTPacket 221 | 222 | - (instancetype)initWithMessageId:(int16_t)msgId; 223 | 224 | @end 225 | 226 | // ------------------------------------------ 227 | 228 | #pragma mark - RHMQTTSubscribe 229 | 230 | @interface RHMQTTSubscribe : RHMQTTPacket 231 | 232 | + (RHMQTTSubscribe *)subscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic qos:(RHMQTTQosLevel)qos; 233 | 234 | @end 235 | 236 | // ------------------------------------------ 237 | 238 | #pragma mark - RHMQTTUnsubscribe 239 | 240 | @interface RHMQTTUnsubscribe : RHMQTTPacket 241 | 242 | + (RHMQTTUnsubscribe *)unsubscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic; 243 | 244 | @end 245 | 246 | // ------------------------------------------ 247 | 248 | #pragma mark - RHMQTTPingReq 249 | 250 | @interface RHMQTTPingReq : RHMQTTPacket 251 | 252 | @end 253 | 254 | // ------------------------------------------ 255 | 256 | #pragma mark - RHMQTTDisconnect 257 | 258 | @interface RHMQTTDisconnect : RHMQTTPacket 259 | 260 | @end 261 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTClient.m 3 | // Pods 4 | // 5 | // Created by ruhong zhu on 2021/8/21. 6 | // 7 | 8 | #import "RHMQTTClient.h" 9 | #import "RHMQTTDecoder.h" 10 | #import "RHMQTTEncoder.h" 11 | 12 | @interface RHMQTTClient () 13 | 14 | @property (nonatomic, strong) RHChannelService *tcpChannel; 15 | @property (nonatomic, strong) RHSocketConnectParam *connectParam; 16 | 17 | @end 18 | 19 | @implementation RHMQTTClient 20 | 21 | - (instancetype)init 22 | { 23 | self = [super init]; 24 | if (self) { 25 | _tcpChannel = [[RHChannelService alloc] init]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)startWithHost:(NSString *)host port:(int)port 31 | { 32 | self.connectParam = [[RHSocketConnectParam alloc] init]; 33 | self.connectParam.host = host; 34 | self.connectParam.port = port; 35 | self.connectParam.heartbeatInterval = 15; 36 | self.connectParam.heartbeatEnabled = YES; 37 | self.connectParam.autoReconnect = NO; 38 | 39 | RHChannelBeats *beats = [[RHChannelBeats alloc] init]; 40 | beats.heartbeatBlock = ^id{ 41 | return [[RHMQTTPingReq alloc] init]; 42 | }; 43 | 44 | [self.tcpChannel.channel addDelegate:self]; 45 | [self.tcpChannel startWithConfig:^(RHChannelConfig *config) { 46 | config.encoder = [[RHMQTTEncoder alloc] init]; 47 | config.decoder = [[RHMQTTDecoder alloc] init]; 48 | config.channelBeats = beats; 49 | config.connectParam = self.connectParam; 50 | }]; 51 | } 52 | 53 | - (void)stop 54 | { 55 | [self.tcpChannel stopService]; 56 | } 57 | 58 | - (void)asyncSendPacket:(RHMQTTPacket *)packet 59 | { 60 | [self.tcpChannel asyncSendPacket:packet]; 61 | } 62 | 63 | #pragma mark - RHSocketChannelDelegate 64 | 65 | - (void)channelOpened:(RHSocketChannel *)channel host:(NSString *)host port:(int)port 66 | { 67 | RHSocketLog(@"[RHMQTT] channelOpened: %@:%@", host, @(port)); 68 | 69 | //需要在password_file.conf文件中设置帐号密码 70 | NSString *username = @"";//@"testuser"; 71 | NSString *password = @"";//@"testuser"; 72 | RHMQTTConnect *connect = [RHMQTTConnect connectWithClientId:@"RHMQTTKit" username:username password:password keepAlive:60 cleanSession:YES]; 73 | [self asyncSendPacket:connect]; 74 | } 75 | 76 | - (void)channelClosed:(RHSocketChannel *)channel error:(NSError *)error 77 | { 78 | RHSocketLog(@"[RHMQTT] channelClosed: %@", error); 79 | [self.tcpChannel.config.channelBeats stop]; 80 | [self.delegate mqttDisconnect:self error:error]; 81 | } 82 | 83 | - (void)channel:(RHSocketChannel *)channel received:(id)packet 84 | { 85 | if (![packet isKindOfClass:[RHMQTTPacket class]]) { 86 | RHSocketLog(@"[RHMQTT] packet(%@) is not kind of RHMQTTPacket", NSStringFromClass([packet class])); 87 | return; 88 | } 89 | 90 | RHMQTTPacket *mqttPacket = (RHMQTTPacket *)packet; 91 | RHSocketLog(@"[RHMQTT] mqttPacket object: %@", [mqttPacket object]); 92 | [self.delegate mqtt:self received:mqttPacket]; 93 | 94 | NSData *buffer = mqttPacket.remainingLengthData; 95 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:buffer]; 96 | RHMQTTFixedHeader *fixedHeader = mqttPacket.fixedHeader; 97 | switch (fixedHeader.type) { 98 | case RHMQTTMessageTypeConnAck: 99 | { 100 | [self.delegate mqttConnected:self host:self.connectParam.host port:self.connectParam.port]; 101 | RHSocketLog(@"RHMQTTMessageTypeConnAck: %d", fixedHeader.type); 102 | [self.tcpChannel.config.channelBeats start]; 103 | } 104 | break; 105 | case RHMQTTMessageTypePublish: { 106 | RHSocketLog(@"RHMQTTMessageTypePublish: %d", fixedHeader.type); 107 | 108 | RHMQTTPublish *publish = [[RHMQTTPublish alloc] initWithObject:buffer]; 109 | publish.fixedHeader = fixedHeader; 110 | 111 | /** 112 | 兄弟,针对QoS2,为了便于说明,我们先假设一个方向,Server -> Client: 113 | ----PUBLISH---> 114 | <----PUBREC---- 115 | ----PUBREL----> 116 | <----PUBCOMP--- 117 | */ 118 | if (fixedHeader.qos == RHMQTTQosLevelAtMostOnce) { 119 | //at most once 120 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES]; 121 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init]; 122 | variableHeader.topic = [byteBuffer readString:2 length:topicLength]; 123 | int16_t payloadLength = buffer.length - (2 + topicLength); 124 | NSData *msgData = [byteBuffer readData:2 + topicLength length:payloadLength]; 125 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init]; 126 | payload.message = msgData; 127 | 128 | publish.variableHeader = variableHeader; 129 | publish.payload = payload; 130 | } else if (fixedHeader.qos == RHMQTTQosLevelAtLeastOnce) { 131 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES]; 132 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init]; 133 | variableHeader.topic = [byteBuffer readString:2 length:topicLength]; 134 | variableHeader.messageId = [byteBuffer readInt16:2+topicLength endianSwap:YES]; 135 | int16_t payloadLength = buffer.length - (2 + topicLength + 2); 136 | NSData *msgData = [byteBuffer readData:2 + topicLength + 2 length:payloadLength]; 137 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init]; 138 | payload.message = msgData; 139 | 140 | publish.variableHeader = variableHeader; 141 | publish.payload = payload; 142 | 143 | /** 144 | 作为订阅者/服务器接收(QoS level = 1)PUBLISH消息之后对发送者的响应。 145 | */ 146 | RHMQTTPublishAck *publishAck = [[RHMQTTPublishAck alloc] initWithMessageId:variableHeader.messageId]; 147 | [self asyncSendPacket:publishAck]; 148 | } else if (fixedHeader.qos == RHMQTTQosLevelExactlyOnce) { 149 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES]; 150 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init]; 151 | variableHeader.topic = [byteBuffer readString:2 length:topicLength]; 152 | variableHeader.messageId = [byteBuffer readInt16:2+topicLength endianSwap:YES]; 153 | int16_t payloadLength = buffer.length - (2 + topicLength + 2); 154 | NSData *msgData = [byteBuffer readData:2 + topicLength + 2 length:payloadLength]; 155 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init]; 156 | payload.message = msgData; 157 | 158 | publish.variableHeader = variableHeader; 159 | publish.payload = payload; 160 | 161 | /** 162 | 作为订阅者/服务器对QoS level = 2的发布PUBLISH消息的发送方的响应,确认已经收到,为QoS level = 2消息流的第二个消息。 和PUBACK相比,除了消息类型不同外,其它都是一样。 163 | */ 164 | RHMQTTPublishRec *publishRec = [[RHMQTTPublishRec alloc] initWithMessageId:variableHeader.messageId]; 165 | [self asyncSendPacket:publishRec]; 166 | } 167 | 168 | RHSocketLog(@"RHMQTTMessageTypePublish: msgid = %@, topic = %@, msg: %@", @(publish.variableHeader.messageId), publish.variableHeader.topic, [[NSString alloc] initWithData:publish.payload.message encoding:NSUTF8StringEncoding]); 169 | [self.delegate mqtt:self publish:publish]; 170 | } 171 | break; 172 | case RHMQTTMessageTypePubAck: { 173 | RHSocketLog(@"RHMQTTMessageTypePubAck: %d", fixedHeader.type); 174 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init]; 175 | variableHeader.messageId = [byteBuffer readInt16:0 endianSwap:YES]; 176 | RHMQTTPublish *publish = [[RHMQTTPublish alloc] init]; 177 | publish.fixedHeader = fixedHeader; 178 | publish.variableHeader = variableHeader; 179 | } 180 | break; 181 | case RHMQTTMessageTypePubRec: 182 | { 183 | //qos = RHMQTTQosLevelExactlyOnce 184 | RHSocketLog(@"RHMQTTMessageTypePubRec: %d", fixedHeader.type); 185 | UInt16 msgId = [byteBuffer readInt16:0 endianSwap:YES]; 186 | RHSocketLog(@"msgId: %d, ", msgId); 187 | } 188 | break; 189 | case RHMQTTMessageTypeSubAck: 190 | { 191 | UInt16 msgId = [byteBuffer readInt16:0 endianSwap:YES]; 192 | NSData *grantedQos = [byteBuffer readData:2 length:byteBuffer.length - 2]; 193 | RHSocketLog(@"msgId: %@, grantedQos: %@", @(msgId), grantedQos); 194 | } 195 | break; 196 | case RHMQTTMessageTypePingResp: 197 | { 198 | RHSocketLog(@"RHMQTTMessageTypePingResp: %d", fixedHeader.type); 199 | } 200 | break; 201 | 202 | default: 203 | break; 204 | } 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // RHMQTTKitDemo 4 | // 5 | // Created by zhuruhong on 15/11/16. 6 | // Copyright © 2015年 zhuruhong. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import 12 | 13 | @interface ViewController () 14 | { 15 | UITextField *_hostTextField; 16 | UITextField *_portTextField; 17 | 18 | UIButton *_connectButton; 19 | UIButton *_disconnectButton; 20 | UIButton *_subscribeButton; 21 | UIButton *_unsubscribeButton; 22 | UIButton *_pingReqButton; 23 | UIButton *_publishButton; 24 | 25 | UITextView *_receivedTextView; 26 | } 27 | 28 | @property (nonatomic, strong) RHMQTTClient *mqttClient; 29 | 30 | @end 31 | 32 | @implementation ViewController 33 | 34 | - (void)loadView 35 | { 36 | [super loadView]; 37 | self.mqttClient = [[RHMQTTClient alloc] init]; 38 | self.mqttClient.delegate = self; 39 | } 40 | 41 | - (void)viewDidLoad { 42 | [super viewDidLoad]; 43 | // Do any additional setup after loading the view, typically from a nib. 44 | 45 | _hostTextField = [[UITextField alloc] init]; 46 | _hostTextField.frame = CGRectMake(20, 40, 200, 50); 47 | _hostTextField.borderStyle = UITextBorderStyleRoundedRect; 48 | _hostTextField.font = [UIFont systemFontOfSize:15]; 49 | _hostTextField.text = @"mq.tongxinmao.com"; 50 | [self.view addSubview:_hostTextField]; 51 | 52 | _portTextField = [[UITextField alloc] init]; 53 | _portTextField.frame = CGRectMake(20, CGRectGetMaxY(_hostTextField.frame) + 10, 200, 50); 54 | _portTextField.borderStyle = UITextBorderStyleRoundedRect; 55 | _portTextField.font = [UIFont systemFontOfSize:15]; 56 | _portTextField.text = @"18830"; 57 | [self.view addSubview:_portTextField]; 58 | 59 | _connectButton = [UIButton buttonWithType:UIButtonTypeCustom]; 60 | _connectButton.frame = CGRectMake(20, CGRectGetMaxY(_portTextField.frame) + 20, 130, 40); 61 | _connectButton.layer.borderColor = [UIColor blackColor].CGColor; 62 | _connectButton.layer.borderWidth = 0.5; 63 | _connectButton.layer.masksToBounds = YES; 64 | [_connectButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 65 | [_connectButton setTitle:@"connect" forState:UIControlStateNormal]; 66 | [_connectButton addTarget:self action:@selector(doConnectButtonAction) forControlEvents:UIControlEventTouchUpInside]; 67 | [self.view addSubview:_connectButton]; 68 | 69 | _subscribeButton = [UIButton buttonWithType:UIButtonTypeCustom]; 70 | _subscribeButton.frame = CGRectMake(20, CGRectGetMaxY(_connectButton.frame) + 20, 130, 40); 71 | _subscribeButton.layer.borderColor = [UIColor blackColor].CGColor; 72 | _subscribeButton.layer.borderWidth = 0.5; 73 | _subscribeButton.layer.masksToBounds = YES; 74 | [_subscribeButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 75 | [_subscribeButton setTitle:@"subscribe" forState:UIControlStateNormal]; 76 | [_subscribeButton addTarget:self action:@selector(doSubscribeButtonAction) forControlEvents:UIControlEventTouchUpInside]; 77 | [self.view addSubview:_subscribeButton]; 78 | 79 | _pingReqButton = [UIButton buttonWithType:UIButtonTypeCustom]; 80 | _pingReqButton.frame = CGRectMake(20, CGRectGetMaxY(_subscribeButton.frame) + 20, 130, 40); 81 | _pingReqButton.layer.borderColor = [UIColor blackColor].CGColor; 82 | _pingReqButton.layer.borderWidth = 0.5; 83 | _pingReqButton.layer.masksToBounds = YES; 84 | [_pingReqButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 85 | [_pingReqButton setTitle:@"ping" forState:UIControlStateNormal]; 86 | [_pingReqButton addTarget:self action:@selector(doPingButtonAction) forControlEvents:UIControlEventTouchUpInside]; 87 | [self.view addSubview:_pingReqButton]; 88 | 89 | // 90 | _disconnectButton = [UIButton buttonWithType:UIButtonTypeCustom]; 91 | _disconnectButton.frame = CGRectMake(160, CGRectGetMaxY(_portTextField.frame) + 20, 130, 40); 92 | _disconnectButton.layer.borderColor = [UIColor blackColor].CGColor; 93 | _disconnectButton.layer.borderWidth = 0.5; 94 | _disconnectButton.layer.masksToBounds = YES; 95 | [_disconnectButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 96 | [_disconnectButton setTitle:@"disconnect" forState:UIControlStateNormal]; 97 | [_disconnectButton addTarget:self action:@selector(doDisconnectButtonAction) forControlEvents:UIControlEventTouchUpInside]; 98 | [self.view addSubview:_disconnectButton]; 99 | 100 | _unsubscribeButton = [UIButton buttonWithType:UIButtonTypeCustom]; 101 | _unsubscribeButton.frame = CGRectMake(160, CGRectGetMaxY(_disconnectButton.frame) + 20, 130, 40); 102 | _unsubscribeButton.layer.borderColor = [UIColor blackColor].CGColor; 103 | _unsubscribeButton.layer.borderWidth = 0.5; 104 | _unsubscribeButton.layer.masksToBounds = YES; 105 | [_unsubscribeButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 106 | [_unsubscribeButton setTitle:@"unsubscribe" forState:UIControlStateNormal]; 107 | [_unsubscribeButton addTarget:self action:@selector(doUnsubscribeButtonAction) forControlEvents:UIControlEventTouchUpInside]; 108 | [self.view addSubview:_unsubscribeButton]; 109 | 110 | _publishButton = [UIButton buttonWithType:UIButtonTypeCustom]; 111 | _publishButton.frame = CGRectMake(160, CGRectGetMaxY(_unsubscribeButton.frame) + 20, 130, 40); 112 | _publishButton.layer.borderColor = [UIColor blackColor].CGColor; 113 | _publishButton.layer.borderWidth = 0.5; 114 | _publishButton.layer.masksToBounds = YES; 115 | [_publishButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; 116 | [_publishButton setTitle:@"publish" forState:UIControlStateNormal]; 117 | [_publishButton addTarget:self action:@selector(doPublishButtonAction) forControlEvents:UIControlEventTouchUpInside]; 118 | [self.view addSubview:_publishButton]; 119 | 120 | CGFloat x = CGRectGetMinX(_connectButton.frame); 121 | CGFloat y = CGRectGetMaxY(_pingReqButton.frame) + 20; 122 | CGFloat width = CGRectGetWidth(self.view.frame) - x - x; 123 | CGFloat height = CGRectGetHeight(self.view.frame) - y - 60; 124 | _receivedTextView = [[UITextView alloc] init]; 125 | _receivedTextView.frame = CGRectMake(x, y, width, height); 126 | _receivedTextView.layer.borderColor = [UIColor blackColor].CGColor; 127 | _receivedTextView.layer.borderWidth = 0.5f; 128 | _receivedTextView.layer.masksToBounds = YES; 129 | _receivedTextView.font = [UIFont systemFontOfSize:20]; 130 | _receivedTextView.text = @"MQTT Log:\n"; 131 | [self.view addSubview:_receivedTextView]; 132 | } 133 | 134 | - (void)doConnectButtonAction 135 | { 136 | NSString *host = _hostTextField.text.length > 5 ? _hostTextField.text : @"127.0.0.1"; 137 | int port = _portTextField.text.length > 1 ? [_portTextField.text intValue] : 1883; 138 | [self.mqttClient startWithHost:host port:port]; 139 | } 140 | 141 | - (void)doSubscribeButtonAction 142 | { 143 | NSString *topic = @"/MQTT/Messanger/#"; 144 | [self showCommand:@"Subscribe" log:topic]; 145 | 146 | //finance/stock/# finance/sotkc/ibm/+ 147 | RHMQTTSubscribe *req = [RHMQTTSubscribe subscribeWithMessageId:3 topic:topic qos:1]; 148 | [self.mqttClient asyncSendPacket:req]; 149 | } 150 | 151 | - (void)doPingButtonAction 152 | { 153 | [self showCommand:@"ping" log:@""]; 154 | 155 | RHMQTTPingReq *req = [[RHMQTTPingReq alloc] init]; 156 | [self.mqttClient asyncSendPacket:req]; 157 | } 158 | 159 | - (void)doDisconnectButtonAction 160 | { 161 | [self showCommand:@"Disconnect" log:@""]; 162 | 163 | RHMQTTDisconnect *req = [[RHMQTTDisconnect alloc] init]; 164 | [self.mqttClient asyncSendPacket:req]; 165 | } 166 | 167 | - (void)doUnsubscribeButtonAction 168 | { 169 | NSString *topic = @"/MQTT/Messanger/#"; 170 | [self showCommand:@"Unsubscribe" log:topic]; 171 | 172 | RHMQTTUnsubscribe *req = [RHMQTTUnsubscribe unsubscribeWithMessageId:22 topic:topic]; 173 | [self.mqttClient asyncSendPacket:req]; 174 | } 175 | 176 | - (void)doPublishButtonAction 177 | { 178 | NSString *topic = @"/MQTT/Messanger/rh"; 179 | [self showCommand:@"Publish" log:topic]; 180 | 181 | RHMQTTPublish *req = [[RHMQTTPublish alloc] init]; 182 | req.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce;//RHMQTTQosLevelExactlyOnce 183 | req.variableHeader.topic = topic; 184 | req.variableHeader.messageId = 99; 185 | req.payload.message = [@"test publish" dataUsingEncoding:NSUTF8StringEncoding]; 186 | [self.mqttClient asyncSendPacket:req]; 187 | } 188 | 189 | - (void)showCommand:(NSString *)command log:(NSString *)log 190 | { 191 | NSMutableString *logStr = [NSMutableString string]; 192 | [logStr appendFormat:@"%@\n", _receivedTextView.text]; 193 | [logStr appendFormat:@"%@: %@\n", command, log]; 194 | _receivedTextView.text = logStr; 195 | } 196 | 197 | #pragma mark - RHMQTTClientDelegate 198 | 199 | - (void)mqttConnected:(RHMQTTClient *)mqtt host:(NSString *)host port:(int)port 200 | { 201 | _connectButton.hidden = YES; 202 | 203 | NSString *topic = [NSString stringWithFormat:@"%@:%@", host, @(port)]; 204 | [self showCommand:@"Connected" log:topic]; 205 | } 206 | 207 | - (void)mqttDisconnect:(RHMQTTClient *)mqtt error:(NSError *)error 208 | { 209 | _connectButton.hidden = NO; 210 | 211 | NSString *topic = [NSString stringWithFormat:@"%@", error]; 212 | [self showCommand:@"Disconnect" log:topic]; 213 | } 214 | 215 | - (void)mqtt:(RHMQTTClient *)mqtt received:(RHMQTTPacket *)packet 216 | { 217 | RHMQTTPacket *mqttPacket = (RHMQTTPacket *)packet; 218 | RHSocketLog(@"[RHMQTT] mqttPacket object: %@", [mqttPacket object]); 219 | 220 | NSData *buffer = mqttPacket.object; 221 | NSData *remainingData = mqttPacket.remainingLengthData; 222 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:remainingData]; 223 | RHMQTTFixedHeader *fixedHeader = mqttPacket.fixedHeader; 224 | switch (fixedHeader.type) { 225 | case RHMQTTMessageTypeConnAck: 226 | { 227 | RHSocketLog(@"RHMQTTMessageTypeConnAck: %d", fixedHeader.type); 228 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 229 | [self showCommand:@"ReceivedData ConnAck" log:topic]; 230 | } 231 | break; 232 | case RHMQTTMessageTypePublish: { 233 | RHSocketLog(@"RHMQTTMessageTypePublish: %d", fixedHeader.type); 234 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 235 | [self showCommand:@"ReceivedData Publish" log:topic]; 236 | } 237 | break; 238 | case RHMQTTMessageTypePubAck: { 239 | //qos = RHMQTTQosLevelAtLeastOnce 240 | RHSocketLog(@"RHMQTTMessageTypePubAck: %d", fixedHeader.type); 241 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 242 | [self showCommand:@"ReceivedData PubAck" log:topic]; 243 | 244 | } 245 | break; 246 | case RHMQTTMessageTypePubRec: { 247 | //qos = RHMQTTQosLevelExactlyOnce 248 | RHSocketLog(@"RHMQTTMessageTypePubRec: %d", fixedHeader.type); 249 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 250 | [self showCommand:@"ReceivedData PubRec" log:topic]; 251 | 252 | } 253 | break; 254 | case RHMQTTMessageTypePubRel: { 255 | RHSocketLog(@"RHMQTTMessageTypePubRel: %d", fixedHeader.type); 256 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 257 | [self showCommand:@"ReceivedData PubRel" log:topic]; 258 | } 259 | break; 260 | case RHMQTTMessageTypePubComp: { 261 | RHSocketLog(@"RHMQTTMessageTypePubComp: %d", fixedHeader.type); 262 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 263 | [self showCommand:@"ReceivedData PubComp" log:topic]; 264 | } 265 | break; 266 | case RHMQTTMessageTypeSubAck: 267 | { 268 | RHSocketLog(@"RHMQTTMessageTypeSubAck: %d", fixedHeader.type); 269 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 270 | [self showCommand:@"ReceivedData SubAck" log:topic]; 271 | 272 | } 273 | break; 274 | case RHMQTTMessageTypePingResp: 275 | { 276 | RHSocketLog(@"RHMQTTMessageTypePingResp: %d", fixedHeader.type); 277 | NSString *topic = [RHSocketUtils hexStringFromData:buffer]; 278 | [self showCommand:@"ReceivedData PingResp" log:topic]; 279 | } 280 | break; 281 | 282 | default: 283 | break; 284 | } 285 | } 286 | 287 | - (void)mqtt:(RHMQTTClient *)mqtt publish:(RHMQTTPublish *)publish 288 | { 289 | NSString *topic = [[NSString alloc] initWithData:publish.payload.message encoding:NSUTF8StringEncoding]; 290 | [self showCommand:@"Received publish message" log:topic]; 291 | } 292 | 293 | @end 294 | -------------------------------------------------------------------------------- /RHMQTTKit/RHMQTTPacket.m: -------------------------------------------------------------------------------- 1 | // 2 | // RHMQTTPacket.m 3 | // Pods 4 | // 5 | // Created by ruhong zhu on 2021/8/21. 6 | // 7 | 8 | #import "RHMQTTPacket.h" 9 | 10 | // ------------------------------------------ 11 | 12 | #pragma mark - RHMQTTFixedHeader 13 | 14 | @implementation RHMQTTFixedHeader 15 | 16 | - (instancetype)initWithByte:(UInt8)byte 17 | { 18 | if (self = [super init]) { 19 | UInt8 header = byte; 20 | _retainFlag = header & 0x01; 21 | _qos = (header & 0x06) >> 1; 22 | _dupFlag = (header & 0x08) >> 3; 23 | _type = (header & 0xf0) >> 4; 24 | } 25 | return self; 26 | } 27 | 28 | @end 29 | 30 | // ------------------------------------------ 31 | 32 | #pragma mark - RHMQTTVariableHeader 33 | 34 | @implementation RHMQTTVariableHeader 35 | 36 | @synthesize object = _object; 37 | 38 | - (instancetype)init 39 | { 40 | if (self = [super init]) { 41 | _name = @"MQIsdp"; 42 | _version = 3; 43 | _connectFlags = 0x02; 44 | _keepAlive = 60; 45 | } 46 | return self; 47 | } 48 | 49 | - (instancetype)initWithObject:(id)aObject 50 | { 51 | if (self = [super init]) { 52 | _object = aObject; 53 | } 54 | return self; 55 | } 56 | 57 | @end 58 | 59 | // ------------------------------------------ 60 | 61 | #pragma mark - RHMQTTTopic 62 | 63 | @implementation RHMQTTTopic 64 | 65 | - (instancetype)init 66 | { 67 | if (self = [super init]) { 68 | // 69 | } 70 | return self; 71 | } 72 | 73 | @end 74 | 75 | // ------------------------------------------ 76 | 77 | #pragma mark - RHMQTTPayload 78 | 79 | @implementation RHMQTTPayload 80 | 81 | - (instancetype)init 82 | { 83 | if (self = [super init]) { 84 | _clientId = @"zrh"; 85 | } 86 | return self; 87 | } 88 | 89 | @end 90 | 91 | // ------------------------------------------ 92 | 93 | #pragma mark - RHMQTTPacket 94 | 95 | @implementation RHMQTTPacket 96 | 97 | - (instancetype)init 98 | { 99 | if (self = [super init]) { 100 | _fixedHeader = [[RHMQTTFixedHeader alloc] init]; 101 | _variableHeader = [[RHMQTTVariableHeader alloc] init]; 102 | _payload = [[RHMQTTPayload alloc] init]; 103 | } 104 | return self; 105 | } 106 | 107 | - (NSData *)dataWithFixedHeader 108 | { 109 | NSMutableData *buffer = [[NSMutableData alloc] init]; 110 | UInt8 header = self.fixedHeader.type << 4; 111 | header |= self.fixedHeader.dupFlag ? 0x08 : 0x00; 112 | header |= self.fixedHeader.qos << 1; 113 | header |= self.fixedHeader.retainFlag ? 0x01 : 0x00; 114 | [buffer appendBytes:&header length:1]; 115 | return buffer; 116 | } 117 | 118 | - (NSData *)dataWithVariableHeader 119 | { 120 | return nil; 121 | } 122 | 123 | - (NSData *)dataWithPayload 124 | { 125 | return nil; 126 | } 127 | 128 | - (NSData *)data 129 | { 130 | NSMutableData *buffer = [[NSMutableData alloc] init]; 131 | [buffer appendData:[self dataWithFixedHeader]]; 132 | return buffer; 133 | } 134 | 135 | @end 136 | 137 | // ------------------------------------------ 138 | 139 | #pragma mark - RHMQTTConnect 140 | 141 | @implementation RHMQTTConnect 142 | 143 | + (RHMQTTConnect *)connectWithClientId:(NSString *)clientId username:(NSString *)username password:(NSString *)password keepAlive:(UInt16)keepAlive cleanSession:(BOOL)cleanSession 144 | { 145 | RHMQTTConnect *connect = [[RHMQTTConnect alloc] init]; 146 | connect.variableHeader.keepAlive = keepAlive; 147 | connect.variableHeader.connectFlags = cleanSession ? 0x02 : 0x00; 148 | connect.payload.clientId = clientId; 149 | connect.payload.username = username; 150 | connect.payload.password = password; 151 | if (username.length > 0) { 152 | connect.variableHeader.connectFlags = connect.variableHeader.connectFlags | 0x80; 153 | } 154 | if (password.length > 0) { 155 | connect.variableHeader.connectFlags = connect.variableHeader.connectFlags | 0x40; 156 | } 157 | return connect; 158 | } 159 | 160 | - (instancetype)init 161 | { 162 | if (self = [super init]) { 163 | self.fixedHeader.type = RHMQTTMessageTypeConnect; 164 | } 165 | return self; 166 | } 167 | 168 | - (NSData *)dataWithVariableHeader 169 | { 170 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 171 | [byteBuffer writeInt16:self.variableHeader.name.length endianSwap:YES]; 172 | [byteBuffer writeString:self.variableHeader.name]; 173 | [byteBuffer writeInt8:self.variableHeader.version]; 174 | [byteBuffer writeInt8:self.variableHeader.connectFlags]; 175 | [byteBuffer writeInt16:self.variableHeader.keepAlive endianSwap:YES]; 176 | return [byteBuffer data]; 177 | } 178 | 179 | - (NSData *)dataWithPayload 180 | { 181 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 182 | 183 | [byteBuffer writeInt16:self.payload.clientId.length endianSwap:YES]; 184 | [byteBuffer writeString:self.payload.clientId]; 185 | 186 | if (self.payload.username.length > 0) { 187 | [byteBuffer writeInt16:self.payload.username.length endianSwap:YES]; 188 | [byteBuffer writeString:self.payload.username]; 189 | } 190 | 191 | if (self.payload.password.length > 0) { 192 | [byteBuffer writeInt16:self.payload.password.length endianSwap:YES]; 193 | [byteBuffer writeString:self.payload.password]; 194 | } 195 | 196 | return [byteBuffer data]; 197 | } 198 | 199 | - (NSData *)data 200 | { 201 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 202 | [byteBuffer writeData:[self dataWithFixedHeader]]; 203 | 204 | NSData *variableHeaderData = [self dataWithVariableHeader]; 205 | NSData *payloadData = [self dataWithPayload]; 206 | 207 | //remaining length 208 | NSUInteger length = variableHeaderData.length + payloadData.length; 209 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]]; 210 | [byteBuffer writeData:variableHeaderData]; 211 | [byteBuffer writeData:payloadData]; 212 | 213 | return [byteBuffer data]; 214 | } 215 | 216 | @end 217 | 218 | // ------------------------------------------ 219 | 220 | #pragma mark - RHMQTTPublish 221 | 222 | @implementation RHMQTTPublish 223 | 224 | - (instancetype)init 225 | { 226 | if (self = [super init]) { 227 | self.fixedHeader.type = RHMQTTMessageTypePublish; 228 | self.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce; 229 | } 230 | return self; 231 | } 232 | 233 | - (NSData *)dataWithVariableHeader 234 | { 235 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 236 | [byteBuffer writeInt16:self.variableHeader.topic.length endianSwap:YES]; 237 | [byteBuffer writeString:self.variableHeader.topic]; 238 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 239 | return [byteBuffer data]; 240 | } 241 | 242 | - (NSData *)dataWithPayload 243 | { 244 | NSMutableData *buffer = [[NSMutableData alloc] init]; 245 | 246 | if (self.payload.message.length > 0) { 247 | [buffer appendData:self.payload.message]; 248 | }//if 249 | 250 | return buffer; 251 | } 252 | 253 | - (NSData *)data 254 | { 255 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 256 | [byteBuffer writeData:[self dataWithFixedHeader]]; 257 | 258 | NSData *variableHeaderData = [self dataWithVariableHeader]; 259 | NSData *payloadData = [self dataWithPayload]; 260 | 261 | //remaining length 262 | NSUInteger length = variableHeaderData.length + payloadData.length; 263 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]]; 264 | [byteBuffer writeData:variableHeaderData]; 265 | [byteBuffer writeData:payloadData]; 266 | 267 | return [byteBuffer data]; 268 | } 269 | 270 | @end 271 | 272 | @implementation RHMQTTPublishAck 273 | 274 | - (instancetype)initWithMessageId:(int16_t)msgId 275 | { 276 | if (self = [super init]) { 277 | self.fixedHeader.type = RHMQTTMessageTypePubAck; 278 | self.variableHeader.messageId = msgId; 279 | } 280 | return self; 281 | } 282 | 283 | - (NSData *)data 284 | { 285 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 286 | [byteBuffer writeData:[self dataWithFixedHeader]]; 287 | [byteBuffer writeInt8:2]; 288 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 289 | return [byteBuffer data]; 290 | } 291 | 292 | @end 293 | 294 | @implementation RHMQTTPublishRec 295 | 296 | - (instancetype)initWithMessageId:(int16_t)msgId 297 | { 298 | if (self = [super init]) { 299 | self.fixedHeader.type = RHMQTTMessageTypePubRec; 300 | self.variableHeader.messageId = msgId; 301 | } 302 | return self; 303 | } 304 | 305 | - (NSData *)data 306 | { 307 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 308 | [byteBuffer writeData:[self dataWithFixedHeader]]; 309 | [byteBuffer writeInt8:2]; 310 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 311 | return [byteBuffer data]; 312 | } 313 | 314 | @end 315 | 316 | @implementation RHMQTTPublishRel 317 | 318 | - (instancetype)initWithMessageId:(int16_t)msgId 319 | { 320 | if (self = [super init]) { 321 | self.fixedHeader.type = RHMQTTMessageTypePubRel; 322 | self.variableHeader.messageId = msgId; 323 | } 324 | return self; 325 | } 326 | 327 | - (NSData *)data 328 | { 329 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 330 | [byteBuffer writeData:[self dataWithFixedHeader]]; 331 | [byteBuffer writeInt8:2]; 332 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 333 | return [byteBuffer data]; 334 | } 335 | 336 | @end 337 | 338 | @implementation RHMQTTPublishComp 339 | 340 | - (instancetype)initWithMessageId:(int16_t)msgId 341 | { 342 | if (self = [super init]) { 343 | self.fixedHeader.type = RHMQTTMessageTypePubComp; 344 | self.variableHeader.messageId = msgId; 345 | } 346 | return self; 347 | } 348 | 349 | - (NSData *)data 350 | { 351 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 352 | [byteBuffer writeData:[self dataWithFixedHeader]]; 353 | [byteBuffer writeInt8:2]; 354 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 355 | return [byteBuffer data]; 356 | } 357 | 358 | @end 359 | 360 | // ------------------------------------------ 361 | 362 | #pragma mark - RHMQTTSubscribe 363 | 364 | @implementation RHMQTTSubscribe 365 | 366 | + (RHMQTTSubscribe *)subscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic qos:(RHMQTTQosLevel)qos 367 | { 368 | RHMQTTTopic *payloadTopic = [[RHMQTTTopic alloc] init]; 369 | payloadTopic.topic = topic; 370 | payloadTopic.qos = qos; 371 | 372 | RHMQTTSubscribe *subscribe = [[RHMQTTSubscribe alloc] init]; 373 | subscribe.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce; 374 | subscribe.variableHeader.messageId = msgId; 375 | subscribe.payload.topics = @[payloadTopic]; 376 | return subscribe; 377 | } 378 | 379 | - (instancetype)init 380 | { 381 | if (self = [super init]) { 382 | self.fixedHeader.type = RHMQTTMessageTypeSubscribe; 383 | } 384 | return self; 385 | } 386 | 387 | - (NSData *)dataWithVariableHeader 388 | { 389 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 390 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 391 | return [byteBuffer data]; 392 | } 393 | 394 | - (NSData *)dataWithPayload 395 | { 396 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 397 | [self.payload.topics enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 398 | RHMQTTTopic *temp = obj; 399 | [byteBuffer writeInt16:temp.topic.length endianSwap:YES]; 400 | [byteBuffer writeString:temp.topic]; 401 | [byteBuffer writeInt8:temp.qos]; 402 | }]; 403 | return [byteBuffer data]; 404 | } 405 | 406 | - (NSData *)data 407 | { 408 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 409 | [byteBuffer writeData:[self dataWithFixedHeader]]; 410 | 411 | NSData *variableHeaderData = [self dataWithVariableHeader]; 412 | NSData *payloadData = [self dataWithPayload]; 413 | 414 | //remaining length 415 | NSUInteger length = variableHeaderData.length + payloadData.length; 416 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]]; 417 | [byteBuffer writeData:variableHeaderData]; 418 | [byteBuffer writeData:payloadData]; 419 | 420 | return [byteBuffer data]; 421 | } 422 | 423 | @end 424 | 425 | // ------------------------------------------ 426 | 427 | #pragma mark - RHMQTTUnsubscribe 428 | 429 | @implementation RHMQTTUnsubscribe 430 | 431 | + (RHMQTTUnsubscribe *)unsubscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic 432 | { 433 | RHMQTTTopic *payloadTopic = [[RHMQTTTopic alloc] init]; 434 | payloadTopic.topic = topic; 435 | 436 | RHMQTTUnsubscribe *unsubscribe = [[RHMQTTUnsubscribe alloc] init]; 437 | unsubscribe.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce; 438 | unsubscribe.variableHeader.messageId = msgId; 439 | unsubscribe.payload.topics = @[payloadTopic]; 440 | return unsubscribe; 441 | } 442 | 443 | - (instancetype)init 444 | { 445 | if (self = [super init]) { 446 | self.fixedHeader.type = RHMQTTMessageTypeUnsubscribe; 447 | } 448 | return self; 449 | } 450 | 451 | - (NSData *)dataWithVariableHeader 452 | { 453 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 454 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES]; 455 | return [byteBuffer data]; 456 | } 457 | 458 | - (NSData *)dataWithPayload 459 | { 460 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 461 | [self.payload.topics enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 462 | RHMQTTTopic *temp = obj; 463 | [byteBuffer writeInt16:temp.topic.length endianSwap:YES]; 464 | [byteBuffer writeString:temp.topic]; 465 | }]; 466 | return [byteBuffer data]; 467 | } 468 | 469 | - (NSData *)data 470 | { 471 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 472 | [byteBuffer writeData:[self dataWithFixedHeader]]; 473 | 474 | NSData *variableHeaderData = [self dataWithVariableHeader]; 475 | NSData *payloadData = [self dataWithPayload]; 476 | 477 | //remaining length 478 | NSUInteger length = variableHeaderData.length + payloadData.length; 479 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]]; 480 | [byteBuffer writeData:variableHeaderData]; 481 | [byteBuffer writeData:payloadData]; 482 | 483 | return [byteBuffer data]; 484 | } 485 | 486 | @end 487 | 488 | // ------------------------------------------ 489 | 490 | #pragma mark - RHMQTTPingReq 491 | 492 | @implementation RHMQTTPingReq 493 | 494 | - (instancetype)init 495 | { 496 | if (self = [super init]) { 497 | self.fixedHeader.type = RHMQTTMessageTypePingReq; 498 | } 499 | return self; 500 | } 501 | 502 | - (NSData *)data 503 | { 504 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 505 | [byteBuffer writeData:[self dataWithFixedHeader]]; 506 | [byteBuffer writeInt8:0]; 507 | return [byteBuffer data]; 508 | } 509 | 510 | @end 511 | 512 | // ------------------------------------------ 513 | 514 | #pragma mark - RHMQTTDisconnect 515 | 516 | @implementation RHMQTTDisconnect 517 | 518 | - (instancetype)init 519 | { 520 | if (self = [super init]) { 521 | self.fixedHeader.type = RHMQTTMessageTypeDisconnect; 522 | } 523 | return self; 524 | } 525 | 526 | - (NSData *)data 527 | { 528 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init]; 529 | [byteBuffer writeData:[self dataWithFixedHeader]]; 530 | [byteBuffer writeInt8:0]; 531 | return [byteBuffer data]; 532 | } 533 | 534 | @end 535 | -------------------------------------------------------------------------------- /RHMQTTKitDemo/RHMQTTKitDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 32EA50CFF73D221668F11622 /* libPods-RHMQTTKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */; }; 11 | 83C446501BF9FAE200065AAC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C4464F1BF9FAE200065AAC /* main.m */; }; 12 | 83C446531BF9FAE200065AAC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C446521BF9FAE200065AAC /* AppDelegate.m */; }; 13 | 83C446561BF9FAE200065AAC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C446551BF9FAE200065AAC /* ViewController.m */; }; 14 | 83C446591BF9FAE200065AAC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83C446571BF9FAE200065AAC /* Main.storyboard */; }; 15 | 83C4465B1BF9FAE200065AAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83C4465A1BF9FAE200065AAC /* Assets.xcassets */; }; 16 | 83C4465E1BF9FAE200065AAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RHMQTTKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RHMQTTKitDemo/Pods-RHMQTTKitDemo.debug.xcconfig"; sourceTree = ""; }; 21 | 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RHMQTTKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-RHMQTTKitDemo/Pods-RHMQTTKitDemo.release.xcconfig"; sourceTree = ""; }; 22 | 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RHMQTTKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 83C4464F1BF9FAE200065AAC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 24 | 83C446511BF9FAE200065AAC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | 83C446521BF9FAE200065AAC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | 83C446541BF9FAE200065AAC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | 83C446551BF9FAE200065AAC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | 83C446581BF9FAE200065AAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | 83C4465A1BF9FAE200065AAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | 83C4465D1BF9FAE200065AAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | 83C4465F1BF9FAE200065AAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RHMQTTKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 83C446481BF9FAE200065AAC /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | 32EA50CFF73D221668F11622 /* libPods-RHMQTTKitDemo.a in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 3D1D7B80482838CD42724940 /* Frameworks */ = { 48 | isa = PBXGroup; 49 | children = ( 50 | FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */, 51 | ); 52 | name = Frameworks; 53 | sourceTree = ""; 54 | }; 55 | 83C446421BF9FAE200065AAC = { 56 | isa = PBXGroup; 57 | children = ( 58 | 83C4464D1BF9FAE200065AAC /* RHMQTTKitDemo */, 59 | 83C4464C1BF9FAE200065AAC /* Products */, 60 | B942A8773E406570646AF5C7 /* Pods */, 61 | 3D1D7B80482838CD42724940 /* Frameworks */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | 83C4464C1BF9FAE200065AAC /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | 83C4464D1BF9FAE200065AAC /* RHMQTTKitDemo */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 83C446511BF9FAE200065AAC /* AppDelegate.h */, 77 | 83C446521BF9FAE200065AAC /* AppDelegate.m */, 78 | 83C446541BF9FAE200065AAC /* ViewController.h */, 79 | 83C446551BF9FAE200065AAC /* ViewController.m */, 80 | 83C446571BF9FAE200065AAC /* Main.storyboard */, 81 | 83C4465A1BF9FAE200065AAC /* Assets.xcassets */, 82 | 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */, 83 | 83C4465F1BF9FAE200065AAC /* Info.plist */, 84 | 83C4464E1BF9FAE200065AAC /* Supporting Files */, 85 | ); 86 | path = RHMQTTKitDemo; 87 | sourceTree = ""; 88 | }; 89 | 83C4464E1BF9FAE200065AAC /* Supporting Files */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 83C4464F1BF9FAE200065AAC /* main.m */, 93 | ); 94 | name = "Supporting Files"; 95 | sourceTree = ""; 96 | }; 97 | B942A8773E406570646AF5C7 /* Pods */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */, 101 | 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */, 102 | ); 103 | name = Pods; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | 83C4464A1BF9FAE200065AAC /* RHMQTTKitDemo */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = 83C446621BF9FAE200065AAC /* Build configuration list for PBXNativeTarget "RHMQTTKitDemo" */; 112 | buildPhases = ( 113 | 54DA4D34A04F5F00C988D100 /* [CP] Check Pods Manifest.lock */, 114 | 83C446471BF9FAE200065AAC /* Sources */, 115 | 83C446481BF9FAE200065AAC /* Frameworks */, 116 | 83C446491BF9FAE200065AAC /* Resources */, 117 | ); 118 | buildRules = ( 119 | ); 120 | dependencies = ( 121 | ); 122 | name = RHMQTTKitDemo; 123 | productName = RHMQTTKitDemo; 124 | productReference = 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */; 125 | productType = "com.apple.product-type.application"; 126 | }; 127 | /* End PBXNativeTarget section */ 128 | 129 | /* Begin PBXProject section */ 130 | 83C446431BF9FAE200065AAC /* Project object */ = { 131 | isa = PBXProject; 132 | attributes = { 133 | LastUpgradeCheck = 0720; 134 | ORGANIZATIONNAME = zhuruhong; 135 | TargetAttributes = { 136 | 83C4464A1BF9FAE200065AAC = { 137 | CreatedOnToolsVersion = 7.2; 138 | DevelopmentTeam = 764XM498XX; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = 83C446461BF9FAE200065AAC /* Build configuration list for PBXProject "RHMQTTKitDemo" */; 143 | compatibilityVersion = "Xcode 3.2"; 144 | developmentRegion = English; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | English, 148 | en, 149 | Base, 150 | ); 151 | mainGroup = 83C446421BF9FAE200065AAC; 152 | productRefGroup = 83C4464C1BF9FAE200065AAC /* Products */; 153 | projectDirPath = ""; 154 | projectRoot = ""; 155 | targets = ( 156 | 83C4464A1BF9FAE200065AAC /* RHMQTTKitDemo */, 157 | ); 158 | }; 159 | /* End PBXProject section */ 160 | 161 | /* Begin PBXResourcesBuildPhase section */ 162 | 83C446491BF9FAE200065AAC /* Resources */ = { 163 | isa = PBXResourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 83C4465E1BF9FAE200065AAC /* LaunchScreen.storyboard in Resources */, 167 | 83C4465B1BF9FAE200065AAC /* Assets.xcassets in Resources */, 168 | 83C446591BF9FAE200065AAC /* Main.storyboard in Resources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXResourcesBuildPhase section */ 173 | 174 | /* Begin PBXShellScriptBuildPhase section */ 175 | 54DA4D34A04F5F00C988D100 /* [CP] Check Pods Manifest.lock */ = { 176 | isa = PBXShellScriptBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | ); 180 | inputPaths = ( 181 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 182 | "${PODS_ROOT}/Manifest.lock", 183 | ); 184 | name = "[CP] Check Pods Manifest.lock"; 185 | outputPaths = ( 186 | "$(DERIVED_FILE_DIR)/Pods-RHMQTTKitDemo-checkManifestLockResult.txt", 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | shellPath = /bin/sh; 190 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 191 | showEnvVarsInLog = 0; 192 | }; 193 | /* End PBXShellScriptBuildPhase section */ 194 | 195 | /* Begin PBXSourcesBuildPhase section */ 196 | 83C446471BF9FAE200065AAC /* Sources */ = { 197 | isa = PBXSourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 83C446561BF9FAE200065AAC /* ViewController.m in Sources */, 201 | 83C446531BF9FAE200065AAC /* AppDelegate.m in Sources */, 202 | 83C446501BF9FAE200065AAC /* main.m in Sources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXSourcesBuildPhase section */ 207 | 208 | /* Begin PBXVariantGroup section */ 209 | 83C446571BF9FAE200065AAC /* Main.storyboard */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | 83C446581BF9FAE200065AAC /* Base */, 213 | ); 214 | name = Main.storyboard; 215 | sourceTree = ""; 216 | }; 217 | 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */ = { 218 | isa = PBXVariantGroup; 219 | children = ( 220 | 83C4465D1BF9FAE200065AAC /* Base */, 221 | ); 222 | name = LaunchScreen.storyboard; 223 | sourceTree = ""; 224 | }; 225 | /* End PBXVariantGroup section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | 83C446601BF9FAE200065AAC /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 233 | CLANG_CXX_LIBRARY = "libc++"; 234 | CLANG_ENABLE_MODULES = YES; 235 | CLANG_ENABLE_OBJC_ARC = YES; 236 | CLANG_WARN_BOOL_CONVERSION = YES; 237 | CLANG_WARN_CONSTANT_CONVERSION = YES; 238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 239 | CLANG_WARN_EMPTY_BODY = YES; 240 | CLANG_WARN_ENUM_CONVERSION = YES; 241 | CLANG_WARN_INT_CONVERSION = YES; 242 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 243 | CLANG_WARN_UNREACHABLE_CODE = YES; 244 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 245 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 246 | COPY_PHASE_STRIP = NO; 247 | DEBUG_INFORMATION_FORMAT = dwarf; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | ENABLE_TESTABILITY = YES; 250 | GCC_C_LANGUAGE_STANDARD = gnu99; 251 | GCC_DYNAMIC_NO_PIC = NO; 252 | GCC_NO_COMMON_BLOCKS = YES; 253 | GCC_OPTIMIZATION_LEVEL = 0; 254 | GCC_PREPROCESSOR_DEFINITIONS = ( 255 | "DEBUG=1", 256 | "$(inherited)", 257 | ); 258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 260 | GCC_WARN_UNDECLARED_SELECTOR = YES; 261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 262 | GCC_WARN_UNUSED_FUNCTION = YES; 263 | GCC_WARN_UNUSED_VARIABLE = YES; 264 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 265 | MTL_ENABLE_DEBUG_INFO = YES; 266 | ONLY_ACTIVE_ARCH = YES; 267 | SDKROOT = iphoneos; 268 | TARGETED_DEVICE_FAMILY = "1,2"; 269 | }; 270 | name = Debug; 271 | }; 272 | 83C446611BF9FAE200065AAC /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu99; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | SDKROOT = iphoneos; 305 | TARGETED_DEVICE_FAMILY = "1,2"; 306 | VALIDATE_PRODUCT = YES; 307 | }; 308 | name = Release; 309 | }; 310 | 83C446631BF9FAE200065AAC /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | baseConfigurationReference = 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */; 313 | buildSettings = { 314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 315 | INFOPLIST_FILE = RHMQTTKitDemo/Info.plist; 316 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 317 | PRODUCT_BUNDLE_IDENTIFIER = com.RHMQTTKitDemo; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | }; 320 | name = Debug; 321 | }; 322 | 83C446641BF9FAE200065AAC /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | baseConfigurationReference = 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */; 325 | buildSettings = { 326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 327 | INFOPLIST_FILE = RHMQTTKitDemo/Info.plist; 328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 329 | PRODUCT_BUNDLE_IDENTIFIER = com.RHMQTTKitDemo; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | }; 332 | name = Release; 333 | }; 334 | /* End XCBuildConfiguration section */ 335 | 336 | /* Begin XCConfigurationList section */ 337 | 83C446461BF9FAE200065AAC /* Build configuration list for PBXProject "RHMQTTKitDemo" */ = { 338 | isa = XCConfigurationList; 339 | buildConfigurations = ( 340 | 83C446601BF9FAE200065AAC /* Debug */, 341 | 83C446611BF9FAE200065AAC /* Release */, 342 | ); 343 | defaultConfigurationIsVisible = 0; 344 | defaultConfigurationName = Release; 345 | }; 346 | 83C446621BF9FAE200065AAC /* Build configuration list for PBXNativeTarget "RHMQTTKitDemo" */ = { 347 | isa = XCConfigurationList; 348 | buildConfigurations = ( 349 | 83C446631BF9FAE200065AAC /* Debug */, 350 | 83C446641BF9FAE200065AAC /* Release */, 351 | ); 352 | defaultConfigurationIsVisible = 0; 353 | defaultConfigurationName = Release; 354 | }; 355 | /* End XCConfigurationList section */ 356 | }; 357 | rootObject = 83C446431BF9FAE200065AAC /* Project object */; 358 | } 359 | --------------------------------------------------------------------------------