iOS開發(fā):XMPP常用組件

核心類 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);
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市糯累,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌效拭,老刑警劉巖胖秒,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阎肝,死亡現(xiàn)場離奇詭異,居然都是意外死亡蚂蕴,警方通過查閱死者的電腦和手機俯邓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門稽鞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朦蕴,“玉大人弟头,你說我怎么就攤上這事≌钊ⅲ” “怎么了伦连?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵惑淳,是天一觀的道長。 經(jīng)常有香客問我移斩,道長,這世上最難降的妖魔是什么忍宋? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任糠排,我火速辦了婚禮超升,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乾闰。我一直安慰自己盈滴,他們只是感情好涯肩,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巢钓,像睡著了一般病苗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上症汹,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天硫朦,我揣著相機與錄音,去河邊找鬼背镇。 笑死咬展,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瞒斩。 我是一名探鬼主播破婆,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼胸囱,長吁一口氣:“原來是場噩夢啊……” “哼祷舀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旺矾,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蔑鹦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后箕宙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嚎朽,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年柬帕,在試婚紗的時候發(fā)現(xiàn)自己被綠了哟忍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狡门。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锅很,靈堂內(nèi)的尸體忽然破棺而出其馏,到底是詐尸還是另有隱情,我是刑警寧澤爆安,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布叛复,位于F島的核電站,受9級特大地震影響扔仓,放射性物質(zhì)發(fā)生泄漏褐奥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一翘簇、第九天 我趴在偏房一處隱蔽的房頂上張望撬码。 院中可真熱鬧,春花似錦版保、人聲如沸呜笑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叫胁。三九已至,卻和暖如春袖裕,著一層夾襖步出監(jiān)牢的瞬間曹抬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工急鳄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人堰酿。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓疾宏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親触创。 傳聞我的和親對象是個殘疾皇子坎藐,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容

  • 搭建完本地服務器之后,我們便可以著手客戶端的工作哼绑,這里我們使用XMPPFramework這個開源庫岩馍,安卓平臺可以使...
    樹下老男孩閱讀 13,525評論 63 134
  • 點擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,772評論 0 15
  • XMPPFramework結(jié)構(gòu) 在進入下一步之前,先給大家講講XMPPFramework的目錄結(jié)構(gòu)抖韩,以便新手們更容...
    啓蘊閱讀 590評論 0 0
  • IM 的實現(xiàn)方式 使用第三方 IM 服務 在國內(nèi)有很多的 IM 第三方服務商蛀恩,底層協(xié)議基本上都是基于 TCP 的,...
    騰飛Tenfay閱讀 3,289評論 1 18
  • 從未想過茂浮,自己的人生會經(jīng)歷這么多双谆,也從未知曉壳咕,自己也可以如此堅強。一步步走過的人生之路顽馋,教會了我太多無法修飾谓厘,...
    憨草包閱讀 273評論 0 1