iOS及时通信 (轻量级的XMPP)-MQTT

原文地址:http://blog.sina.com.cn/s/blog_144141ea00102vgzr.html    

    在写之前,我们首先了解一下为什么iOS维护长连接需要心跳机制,首先我们知道,维护任何一个长连接都需要心跳机制,客户端发送一个心跳给服务器,服务器给客户端一个心跳应答,这样就形成客户端服务器的一次完整的握手,这个握手是让双方都知道他们之间的连接是没有断开,客户端是在线的。如果超过一个时间的阈值,客户端没有收到服务器的应答,或者服务器没有收到客户端的心跳,那么对客户端来说则断开与服务器的连接重新建立一个连接,对服务器来说只要断开这个连接即可。也就是说,如果想要完成及时聊天的功能,我们需要一个订阅和一个发布功能。话不多说,进入主题,先去Github上down一个MQTTKit框架,网址https://github.com/mobile-web-messaging/MQTTKit (需要 如果你们公司连软件都买不起 趁早辞了吧)然后把MQTTKit导入工程

 新建一个类VPKCClientManager

​VPKCClientManager.h代码如下

#import

#import "MQTTKit.h"


@interface VPKCClientManager : NSObject


+ (instancetype)sharedClient;

- (void)subscribeMessageWithMessageHandler:(void(^)(NSDictionary *dict))messageHandler;

 

@end


VPKCClientManager.m代码如下

#import "VPKCClientManager.h"


#define MQTT_HOST @"**************"//MQTT服务器网址 什么?你们后台不会搭建MQTT服务器 呵呵。。


static VPKCClientManager *instance = nil;


@implementation VPKCClientManager

{

    MQTTClient *client;

}


+ (instancetype)sharedClient

{

    //这里用到了单例 个人感觉不用也行

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        instance = [[self allocinit];

    });

    return instance;

}


- (instancetype)init

{

    if (self = [super init]) {

        //这里我们选择了把deviceId作为mqttclientId   port为端口号  username是用户名 password是密码

        NSString *deviceId = [[NSUserDefaultsstandardUserDefaults] objectForKey:@"deviceId"];

        NSString *clientId = [NSString stringWithFormat:@"kc-%@", deviceId];

        client = [[MQTTClient alloc]initWithClientId:clientId];

        client.port = 1883;

        client.username = @"kidcares";

        client.password = @"12345";

    }

    return self;

}

// 重点来了  这里是MQTT的订阅方法 在你需要的地方 调用就行

- (void)subscribeMessageWithMessageHandler:(void(^)(NSDictionary *dict))messageHandler

{


    [client setMessageHandler:^(MQTTMessage *message){

        id json = [NSJSONSerializationJSONObjectWithData:message.payloadoptions:NSJSONReadingAllowFragments error:nil];

        messageHandler(json);

    }];

    

    [client connectToHost:MQTT_HOSTcompletionHandler:^(MQTTConnectionReturnCode code) {

        if (code == ConnectionAccepted) {

            

            [client subscribe:client.clientIDwithCompletionHandler:nil];

        }

    }];

    

}

@end


然后在你要用的控制器中导入#import "VPKCClientManager.h"

创建一个对象static VPKCClientManager *clientManager;

clientManager = [VPKCClientManager sharedClient];//初始化

这里就是MQTT的订阅方法 这个方法执行后 在MQTT服务器上就可以看到你的设备  dict就是从MQTT服务器传下来的内容

 

 [clientManagersubscribeMessageWithMessageHandler:^(NSDictionary *dict) {

   [clientManagersubscribeMessageWithMessageHandler:^(NSDictionary *dict) {

        

        for (id key in dict) {//因为MQTT可以有多种用处,为了区分各个功能这里我们选择了遍历dict 根据key来判断如何做出响应

            if ([key isEqualToString:@"shoutResult"]) {

                

            }

            else if ([key isEqualToString:@"chat"])

            {

                NSDictionary * chat = [dict valueForKey:@"chat"];

                playurl = [chat valueForKey:@"fileUrl"];//这里取出服务器文件的地址

                time = [chat valueForKey:@"fileRunningTime"];

                //这个是文件下载的封装方法 下面会有介绍

                [self.audio downloadTaskURL:playurlcompletion:^(NSURL *fileplay) {

                    [selfperformSelectorOnMainThread:@selector(refreshtable:)withObject:fileplay waitUntilDone:NO];//因为AFN下载涉及到多线程问题,如果不采用主线程加载,UI效果不会刷新

                    self.url = fileplay;

                    

                }];

            }

        }

        

        

    }];


至此 MQTT订阅部分完成了  下面我们来说说MQTT的发布

新建一个类VPKCPush 

VPKCPush.h代码如下

#import

#import "VPKCPushModel.h"


@interface VPKCPush : NSObject


+ (void)pushMessageParameters:(VPKCPushModel *)param success:(void (^)(NSString *msgId))success failure:(void (^)(NSInteger statusCode))failure;


@end


VPKCPush.m代码如下

#import "VPKCPush.h"

#import "MJExtension.h"//一个三方的直接生成keyvaluepairs的方法


@implementation VPKCPush



+ (void)pushMessageParameters:(VPKCPushModel *)param success:(void (^)(NSString *msgId))success failure:(void (^)(NSInteger statusCode))failure

{

    NSString *url = [KIDCARES_HOSTstringByAppendingString:@"/api/v1/push"];

    [VPKCNetworking POSTWithURL:url parameters:[paramkeyValuessuccess:^(AFHTTPRequestOperation *operation, idresponseObject) {

        success([responseObject valueForKey:@"msgId"]);

    } failure:^(AFHTTPRequestOperation *operation, NSError*error) {

        FAILURE;

    }];

}

这里是上传的下载的封装方法  发送消息时调用上传  接受消息调用下载

- (void)fileUploadParameters:(VPKCFileInfo *)file MIMEType:(NSString *)mimeType success:(void (^)(NSString *, NSString*))success failure:(void (^)(NSInteger))failure

{

    NSString *url = [KIDCARES_HOSTstringByAppendingString:@"/api/v1/file"];

    NSDictionary *param = [NSDictionarydictionaryWithObjectsAndKeys:

                           file.expiration@"expiration",

                           file.expires@"expires",

                           file.size@"size"nil];

    NSLog(@"~~~~~~~~~%@",param);

    AFHTTPRequestOperationManager *manager = [VPKCNetworking managerWithURL:url httpMethod:@"POST"];

    [manager POST:url parameters:paramconstructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

        

        [formData appendPartWithFileData:file.dataname:@"file" fileName:file.name mimeType:mimeType];

        NSLog(@"===========%@",formData);

    } success:^(AFHTTPRequestOperation *operation, idresponseObject) {

        NSString *fileUrl = [responseObject valueForKey:@"fileUrl"];

        NSString *fileMd5 = [responseObject valueForKey:@"fileMd5"];

        success(fileUrl, fileMd5);

    } failure:^(AFHTTPRequestOperation *operation, NSError*error) {

        FAILURE;

    }];

}


- (void)fileDownloadUrl:(NSString *)url success:(void (^)(NSString *))success failure:(void (^)(NSInteger))failure

{

    AFHTTPRequestOperationManager *manager = [VPKCNetworking managerWithURL:url httpMethod:@"GET"];

    [manager GET:url parameters:nilsuccess:^(AFHTTPRequestOperation *operation, idresponseObject) {

        NSString *fileName = [NSStringstringWithFormat:@"%.0f.caf", [NSDatetimeIntervalSinceReferenceDate]];

        NSString *filePath = [NSTemporaryDirectory()stringByAppendingPathComponent:fileName];

        NSData *data = [NSData dataWithData:responseObject];

        [data writeToFile:filePath atomically:YES];

        success(filePath);

    } failure:^(AFHTTPRequestOperation *operation, NSError*error) {

        FAILURE;

    }];

}

然后在你要发布MQTT消息的控制器里导入头文件

      [self.audio uploadTaskSuccess:^(NSString *fileUrl, NSString*fileMd5) {//调用文件上传的方法

        VPKCDialogue *request = [[VPKCDialogue allocinit];

        VPKCChat *chat = [[VPKCChat allocinit];

        chat.fileRunningTime = self.audio.duration;

        chat.fileUrl = fileUrl;

        chat.fileMd5 = fileMd5;

        chat.createTime = self.audio.createTime;

        NSString *kidDevice = [[NSUserDefaultsstandardUserDefaults] objectForKey:@"kiddeviceId"];

        NSArray * kidDevicearray = [[NSArray alloc]initWithObjects:kidDevice, nil];

        if ([kidDevicearray count]) {

            model = [[VPKCPushModel allocinitWithShout:chatdevices:@[kidDevice]];

        }

        

        [request shoutParameters:model success:^(NSString*msgId) {

            sendsuccess = [[UILabel alloc]initWithFrame:CGRectMake(self.view.frame.size.width/2-50,self.view.frame.size.height/2,100, 50)];

            sendsuccess.text=@"发送成功";

            sendsuccess.textColor = [UIColor whiteColor];

            sendsuccess.textAlignment=NSTextAlignmentCenter;

            sendsuccess.backgroundColor=[UIColor grayColor];

            sendsuccess.alpha=0.7;

            [self.view addSubview:sendsuccess];

            [NSTimer scheduledTimerWithTimeInterval:2target:self selector:@selector(Timerisover:) userInfo:nilrepeats:NO];

            

            

        } failure:^(NSInteger statusCode) {

            sendfail = [[UILabel alloc]initWithFrame:CGRectMake(self.view.frame.size.width/2-50,self.view.frame.size.height/2,100, 50)];

            sendfail.text=@"发送失败";

            sendfail.textAlignment=NSTextAlignmentCenter;

            sendfail.backgroundColor=[UIColor grayColor];

            sendfail.alpha=0.7;

            [self.view addSubview:sendfail];

            [NSTimer scheduledTimerWithTimeInterval:2target:self selector:@selector(Timerisover1:) userInfo:nilrepeats:NO];

            

        }];

        

    } failure:^(NSInteger statusCode) {

    }];

​至此MQTT订阅发布的流程已经完成 ,最后强调一点,要实现两个设备之间的通信 必须保证两个设备的同时在MQTT server上在线。

你可能感兴趣的:(iOS)