核心類 XMPPStream
客戶端與服務端的交互阱洪,必須通過XMPPStream類隅茎,它提供了很多的API和屬性設置,通過socket來實現(xiàn)俏竞,而模塊類(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
心跳包模塊可以有效刷新用戶狀態(tài),當客戶端或服務端心跳異常時只祠,另一端可以主動斷開連接扰肌。
//屬性
@property (nonatomic, strong) XMPPAutoPing *xmppAutoPing;//定時發(fā)心跳包
@property (nonatomic, assign) NSInteger pingTimeoutCount;//心跳超時次數(shù)
//用法
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包晶府,則清除超時次數(shù)
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
在網(wǎng)絡有波動出現(xiàn)斷線的情況书劝,該模塊能夠自動重連,以便網(wǎng)絡恢復正常后能夠及時連接服務端猾昆。
//屬性
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;//斷線重連
@property (nonatomic, assign) NSInteger reconnectCount;//重連次數(shù)
//用法
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
流管理能夠有效解決消息丟失的情況廊鸥,特別是在網(wǎng)絡出現(xiàn)波動時辖所,服務端不能有效判定用戶狀態(tài)而導致消息轉(zhuǎn)發(fā)出錯缘回。
//屬性
@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];
//用戶鑒權成功后調(diào)用
[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
該模塊主要用于對比客戶端與服務端時間差酥宴。客戶端發(fā)消息時應該使用服務端時間而不是當前設備時間丰滑,以免出現(xiàn)不同設備時區(qū)不同或手動調(diào)整時間導致的聊天消息亂序情況發(fā)生。
//屬性
@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 - 好友數(shù)據(jù)代理 XMPPRosterDelegate
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {
VEYLogInfo(@"??開始獲取好友節(jié)點仆百!");
}
-(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(@"??好友節(jié)點==>%@--%@", 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(@"??結(jié)束獲取好友節(jié)點!");
self.loadFinished = YES;
[[NSNotificationCenter defaultCenter] postNotificationName:kFriendListChangeNotification object:nil];
}
//收到好友訂閱請求的回調(diào)方法
- (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 好友狀態(tài)回調(diào) 上線/下線/取消訂閱
- (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. 消息回執(zhí) XMPPMessageDeliveryReceipts
該模塊主要用于消息接收確認峦朗,防止出現(xiàn)消息丟失的情況,原理是在發(fā)送消息時添加標識翎朱,對方收到消息時回應標識艰亮,發(fā)送方即可確認消息已被接收迄埃。但客戶端需要補充消息校驗策略或者服務端添加消息回執(zhí)插件,否則網(wǎng)絡有波動時服務端出現(xiàn)斷流仍會導致消息丟失侄非。而流管理模塊可以有效解決斷流導致的消息丟失問題逞怨,所以該模塊可以用流管理代替。
//屬性
@property (nonatomic, strong) XMPPMessageDeliveryReceipts *xmppReceipts;//消息回執(zhí)
//用法
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 - 消息回執(zhí)代理 XMPPMessageDeliveryReceiptsDelegate
- (void)xmppMessageDeliveryReceipts:(XMPPMessageDeliveryReceipts *)xmppMessageDeliveryReceipts didReceiveReceiptResponseMessage:(XMPPMessage *)message {
VEYLogInfo(@"??XMPP收到消息回執(zhí)==>MsgId:%@ 已被成功接收", message.receiptResponseID);
}