iOS开发:XMPP常用组件

2020-03-25  本文已影响0人  HiUSB

核心类 XMPPStream

客户端与服务端的交互,必须通过XMPPStream类,它提供了很多的API和属性设置,通过socket来实现,而模块类(XMPPModule)也是依赖于它。

//用法
self.xmppQueue = dispatch_get_main_queue();
_xmppStream = [[XMPPStream alloc] init];
_xmppStream.hostName = kXmppHostName;
_xmppStream.hostPort = kXmppPort;
_xmppStream.enableBackgroundingOnSocket = YES;
_xmppStream.keepAliveInterval = -1;
[_xmppStream addDelegate:self delegateQueue:self.xmppQueue];

模块类 XMPPModule

XMPPFramework提供了许多模块方便使用,它们都是XMPPModule的子类。

1. 心跳包 XMPPAutoPing

心跳包模块可以有效刷新用户状态,当客户端或服务端心跳异常时,另一端可以主动断开连接。

//属性
@property (nonatomic, strong) XMPPAutoPing *xmppAutoPing;//定时发心跳包
@property (nonatomic, assign) NSInteger pingTimeoutCount;//心跳超时次数

//用法
self.xmppAutoPing = [[XMPPAutoPing alloc] init];
self.xmppAutoPing.pingInterval = 10.f; // 心跳包间隔
self.xmppAutoPing.respondsToQueries = YES;
[self.xmppAutoPing addDelegate:self delegateQueue:self.xmppQueue];
[self.xmppAutoPing activate:_xmppStream];

#pragma mark - 心跳包代理 XMPPAutoPingDelegate
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender {
    VEYLogInfo(@"🍎XMPP收到心跳包");
    // 如果至少有1次超时了,再收到ping包,则清除超时次数
    if (self.pingTimeoutCount > 0) {
        self.pingTimeoutCount = 0;
    }
}

- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender {
    VEYLogInfo(@"🍎XMPP心跳超时!");
    // 收到两次超时,就disconnect
    self.pingTimeoutCount++;
    if (self.pingTimeoutCount >= 2) {
        [self.xmppStream disconnect];
    }
}
2. 断线重连 XMPPReconnect

在网络有波动出现断线的情况,该模块能够自动重连,以便网络恢复正常后能够及时连接服务端。

//属性
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;//断线重连
@property (nonatomic, assign) NSInteger reconnectCount;//重连次数

//用法
self.xmppReconnect = [[XMPPReconnect alloc] init];
self.xmppReconnect.autoReconnect = YES;
self.xmppReconnect.reconnectDelay = 0.f;// 一旦失去连接,立马开始自动重连,不延迟
self.xmppReconnect.reconnectTimerInterval = 3.f;// 每隔3秒自动重连一次
[self.xmppReconnect addDelegate:self delegateQueue:self.xmppQueue];
[self.xmppReconnect activate:_xmppStream];

#pragma mark - 自动重连代理 XMPPReconnectDelegate
- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags {
    VEYLogInfo(@"🍎XMPP意外断开连接!开始自动重连!");
}

- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags {
    self.reconnectCount++;
    VEYLogInfo(@"🍎XMPP自动重连...第%@次", @(self.reconnectCount));
    if (self.reconnectCount < 5) {
    } else if (self.reconnectCount >= 5 && self.reconnectCount <= 10) {
//        [self.xmppReconnect resSetupReconnectTimerWithTimerInterval:9.f];
    } else if (self.reconnectCount > 10 && self.reconnectCount <= 15) {
//        [self.xmppReconnect resSetupReconnectTimerWithTimerInterval:15.f];
    } else {
        [self reconnectImmediately];
    }
    return YES;
}

- (void)reconnectImmediately {
    self.xmppReconnect.reconnectTimerInterval = 3.f;
    self.reconnectCount = 0;
    [self.xmppReconnect stop];
    [self.xmppReconnect manualStart];
}
3. 流管理 XMPPStreamManagement

流管理能够有效解决消息丢失的情况,特别是在网络出现波动时,服务端不能有效判定用户状态而导致消息转发出错。

//属性
@property (nonatomic, strong) XMPPStreamManagement *streamManagement;//流管理
@property (nonatomic, strong) XMPPStreamManagementMemoryStorage *streamManagementStorage;//流管理仓库

//用法
self.streamManagementStorage = [[XMPPStreamManagementMemoryStorage alloc] init];
self.streamManagement = [[XMPPStreamManagement alloc] initWithStorage:self.streamManagementStorage dispatchQueue:dispatch_get_global_queue(0, 0)];
self.streamManagement.autoResume = YES;
[self.streamManagement addDelegate:self delegateQueue:self.xmppQueue];
[self.streamManagement activate:_xmppStream];
//用户鉴权成功后调用
[self.streamManagement enableStreamManagementWithResumption:YES maxTimeout:0];

#pragma mark - 流管理代理 XMPPStreamManagementDelegate
- (void)xmppStreamManagement:(XMPPStreamManagement *)sender wasEnabled:(NSXMLElement *)enabled {
    VEYLogInfo(@"🍎XMPP流管理开启==>%@", enabled);
}

- (void)xmppStreamManagement:(XMPPStreamManagement *)sender wasNotEnabled:(NSXMLElement *)failed {
    VEYLogInfo(@"🍎XMPP流管理关闭==>%@", failed);
}
5. 自动时间 XMPPAutoTime

该模块主要用于对比客户端与服务端时间差。客户端发消息时应该使用服务端时间而不是当前设备时间,以免出现不同设备时区不同或手动调整时间导致的聊天消息乱序情况发生。

//属性
@property (nonatomic, strong) XMPPAutoTime *xmppTime;//服务器时间

//用法
self.xmppTime = [[XMPPAutoTime alloc] initWithDispatchQueue:self.xmppQueue];
self.xmppTime.recalibrationInterval = 60;//同步服务器时间间隔
[self.xmppTime addDelegate:self delegateQueue:self.xmppQueue];
[self.xmppTime activate:_xmppStream];

#pragma mark - 自动时间代理 XMPPAutoTimeDelegate
- (void)xmppAutoTime:(XMPPAutoTime *)sender didUpdateTimeDifference:(NSTimeInterval)timeDifference {
    VEYLogInfo(@"🍎XMPP服务端时间==>%@, 与手机时差:%f", [[NSDate date] dateByAddingTimeInterval:timeDifference], timeDifference);
}
6. 花名册(好友列表) XMPPRoster

该模块提供好友列表相关功能,比如添加好友,删除好友,好友上下线等。

//属性
@property (nonatomic, strong) XMPPRoster *roster;//花名册
@property (nonatomic, strong) XMPPRosterCoreDataStorage *rosterCoreDataStorage;//花名册仓库

//用法
self.rosterCoreDataStorage= [XMPPRosterCoreDataStorage sharedInstance];
self.roster = [[XMPPRoster alloc] initWithRosterStorage:self.rosterCoreDataStorage
dispatchQueue:dispatch_get_global_queue(0, 0)];
[self.roster addDelegate:self delegateQueue:self.xmppQueue];
self.roster.autoFetchRoster = NO;
[self.roster activate:self.xmppStream];

#pragma mark - 好友数据代理 XMPPRosterDelegate
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {
    VEYLogInfo(@"🍎开始获取好友节点!");
}

-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item {
    NSString *type = [[item attributeForName:@"subscription"] stringValue];
    XMPPJID *jid = [XMPPJID jidWithString:[[item attributeForName:@"jid"] stringValue]];
    NSString *userName = jid.user;
    VEYLogInfo(@"🍎好友节点==>%@--%@", userName, type);
    
    if ([type isEqualToString:@"both"] || [type isEqualToString:@"from"] || [type isEqualToString:@"to"]) {
        for (XMPPJID *obj in self.friendList) {
            if ([obj.user isEqualToString:userName]) {
                return;
            }
        }
        [self.friendList addObject:jid];
        [[NSNotificationCenter defaultCenter] postNotificationName:kFriendListChangeNotification object:nil];
    } else if ([type isEqualToString:@"remove"]) {
        [self removeFriendListUserId:userName];
    }
}

-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {
    VEYLogInfo(@"🍎结束获取好友节点!");
    self.loadFinished = YES;
    [[NSNotificationCenter defaultCenter] postNotificationName:kFriendListChangeNotification object:nil];
}

//收到好友订阅请求的回调方法
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {
    VEYLogInfo(@"🍎收到订阅==>%@", presence);
    //判断是否重复请求
    for (XMPPJID *jid in self.subscribeList) {
        if ([presence.from.user isEqualToString:jid.user] &&[presence.from.domain isEqualToString:jid.domain]) {
            return;
        }
    }
    [self.subscribeList addObject:presence.from];
    [[NSNotificationCenter defaultCenter] postNotificationName:kSubscribeListChangeNotification object:nil];
}

/**
 * Sent when a Roster Push is received as specified in Section 2.1.6 of RFC 6121.
 **/
- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterPush:(XMPPIQ *)iq {
    VEYLogInfo(@"🍎收到RosterPush==>%@", iq);
}

#pragma mark 好友状态回调 上线/下线/取消订阅
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
    NSString *user = presence.from.user;
    if ([presence.type isEqualToString:@"available"] && ![presence.from isEqual:sender.myJID]) {
        VEYLogInfo(@"🍎好友上线==>%@", user);
        [[NSNotificationCenter defaultCenter] postNotificationName:kUserOnlineNotification object:presence.from];
    } else if ([presence.type isEqualToString:@"unavailable"] && ![presence.from isEqual:sender.myJID]) {
        VEYLogInfo(@"🍎好友离线==>%@", user);
        [[NSNotificationCenter defaultCenter] postNotificationName:kUserOfflineNotification object:presence.from];
    } else if ([presence.type isEqualToString:@"unsubscribe"]) {
        VEYLogInfo(@"🍎好友取消订阅==>%@", user);
        [[NSNotificationCenter defaultCenter] postNotificationName:kUserUnSubscribeNotification object:presence.from];
        [self deleteFriendWithUserId:user];
    }
}
7. 消息回执 XMPPMessageDeliveryReceipts

该模块主要用于消息接收确认,防止出现消息丢失的情况,原理是在发送消息时添加标识,对方收到消息时回应标识,发送方即可确认消息已被接收。但客户端需要补充消息校验策略或者服务端添加消息回执插件,否则网络有波动时服务端出现断流仍会导致消息丢失。而流管理模块可以有效解决断流导致的消息丢失问题,所以该模块可以用流管理代替。

//属性
@property (nonatomic, strong) XMPPMessageDeliveryReceipts *xmppReceipts;//消息回执

//用法
self.xmppReceipts = [[XMPPMessageDeliveryReceipts alloc] initWithDispatchQueue:self.xmppQueue];
self.xmppReceipts.autoSendMessageDeliveryReceipts = YES;
self.xmppReceipts.autoSendMessageDeliveryRequests = YES;
[self.xmppReceipts addDelegate:self delegateQueue:self.xmppQueue];
[self.xmppReceipts activate:_xmppStream];

#pragma mark - 消息回执代理 XMPPMessageDeliveryReceiptsDelegate
- (void)xmppMessageDeliveryReceipts:(XMPPMessageDeliveryReceipts *)xmppMessageDeliveryReceipts didReceiveReceiptResponseMessage:(XMPPMessage *)message {
    VEYLogInfo(@"🍎XMPP收到消息回执==>MsgId:%@ 已被成功接收", message.receiptResponseID);
}
上一篇 下一篇

猜你喜欢

热点阅读