即时通信3
xmpp整理筆記:用戶網(wǎng)絡(luò)連接及好友的管理
xmpp中的用戶連接模塊包括用戶的上線與下線信息展現(xiàn),用戶登錄,用戶的注冊;?好友模塊包括好友的添加,好友的刪除,好友列表的展示。
在xmpp中 負(fù)責(zé)數(shù)據(jù)傳輸?shù)念愂莤mppStream,開發(fā)的過程中,針對不同的傳輸內(nèi)容,會調(diào)用不同的代理方法,在使用XMPPFramework開發(fā)時,只需要在不同的代理方法中,填寫相同的代碼即可。
往期回顧:
xmpp整理筆記:xmppFramework框架的導(dǎo)入和介紹??http://www.cnblogs.com/zhonghuaxiaodangjia/p/4323475.html?
xmpp整理筆記:環(huán)境的快速配置(附安裝包)??http://www.cnblogs.com/zhonghuaxiaodangjia/p/4323460.html
一。大概的連接過程如下
1.運行后需要和服務(wù)器建立一個長連接,系統(tǒng)會反饋鏈接是否成功
2.成功時需要告訴服務(wù)器的用戶的密碼,服務(wù)器判斷是否給予授權(quán)
3.成功授權(quán)后,告訴服務(wù)器上線了。
4.將要離開時告訴服務(wù)器,我需要斷開鏈接了。
5.服務(wù)器反饋你可以斷開了,然后你再告訴服務(wù)器你下線了
?
二。首先,需要知道 ?XMPPStreamDelegate 和 ?XMPPRosterDelegate 的一些代理方法
如果你不是在董鉑然博客園看到本文 請點擊查看原文
xmpp流代理方法:
連接成功時調(diào)用
- (void)xmppStreamDidConnect:(XMPPStream *)sender
斷開連接時調(diào)用
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
授權(quán)成功時調(diào)用
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
授權(quán)失敗時調(diào)用
-(void)xmppStream:(XMPPStream *)sender?didNotAuthenticate:(DDXMLElement *)error
注冊成功時調(diào)用
- (void)xmppStreamDidRegister:(XMPPStream *)sender
注冊失敗時調(diào)用
- (void)xmppStream:(XMPPStream *)sender?didNotRegister:(DDXMLElement *)error
xmppRoster花名冊代理方法?
接收到好友請求時調(diào)用
- (void)xmppRoster:(XMPPRoster *)sender?didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
?
三。用戶的登錄:
用戶需要在連接成功后的代理方法中 將自己的密碼發(fā)送給服務(wù)器,自己的密碼應(yīng)該是在點擊登錄的時候就和其他信息一起存入偏好設(shè)置了,在現(xiàn)在需要的時候可以輕而易舉的取出來。在發(fā)送驗證請求的時候會用到這個方法authenticateWithPassword: 后面的error在實際開發(fā)中建議必須處理,我在這就偷懶了如下所示
| 1 2 3 4 5 6 7 8 9 10 | /** 連接成功時調(diào)用 */ - (void)xmppStreamDidConnect:(XMPPStream *)sender { ????????NSLog(@"連接成功"); ????? ????????NSString?*password = [[NSUserDefaults?standardUserDefaults] valueForKey:SXLoginPasswordKey]; ????????// 將用戶密碼發(fā)送給服務(wù)器,進(jìn)行用戶登錄 ????????[self.xmppStream authenticateWithPassword:password error:NULL]; } |
?然后等待結(jié)果,在授權(quán)成功后來到授權(quán)成功代理方法在這應(yīng)該先告訴服務(wù)器用戶上線,然后給發(fā)出成功通知,自己的AppDelegate在遠(yuǎn)處接收,一旦接收到通知馬上更換應(yīng)用程序的根控制器到進(jìn)入后的界面,這里要注意這些代理方法都是在異步的,所以這里要用到線程間通訊,在主線程發(fā)送通知
| 1 2 3 4 5 6 | // 通知服務(wù)器用戶上線 [self?goOnline]; // 在主線程利用通知發(fā)送廣播 dispatch_async(dispatch_get_main_queue(), ^{ ????[[NSNotificationCenter?defaultCenter] postNotificationName:SXLoginResultNotification object:@(YES)]; }); |
如果授權(quán)失敗的話,應(yīng)該斷開與服務(wù)器的鏈接,并且把開始存儲的用戶偏好清空(因為這些是錯誤的沒用),然后再到主線程更新UI彈出一個框顯示密碼錯誤,并且發(fā)出失敗通知,讓APPDelegate切換根控制器到登錄界面?(董鉑然原創(chuàng))
| 1 2 3 4 5 6 7 8 9 10 11 12 | // 斷開與服務(wù)器的連接 ????[self?disconnect]; ????// 清理用戶偏好 ????[self?clearUserDefaults]; ????// 在主線程更新UI ????if?(self.failed) { ????????dispatch_async(dispatch_get_main_queue(), ^ {self.failed(@"用戶名或者密碼錯誤!");}); ????} ????// 在主線程利用通知發(fā)送廣播 ????dispatch_async(dispatch_get_main_queue(), ^{ ????????[[NSNotificationCenter?defaultCenter] postNotificationName:SXLoginResultNotification object:@(NO)]; ????}); |
?
四。用戶的上線和下線:
關(guān)于用戶的上線和下線,需要用到一個類XMPPPresence 類。這個類是XMPPElement的子類,主要用來管理某些信息的展現(xiàn)。首先要實例化一個對象,這其中會用到一個presenceWithType 方法,有兩個選擇@"unavailable"代表下線,@"available"代表上線,一般情況上線的時候后面就可以直接省略。實例化之后用xmpp流發(fā)出去。如下所示
| 1 2 3 4 5 6 7 8 9 10 11 12 | #pragma mark - ******************** 用戶的上線和下線 - (void)goOnline { ????XMPPPresence *p = [XMPPPresence presence]; ????? ????[self.xmppStream sendElement:p]; } - (void)goOffline { ????XMPPPresence *p = [XMPPPresence presenceWithType:@"unavailable"]; ????? ????[self.xmppStream sendElement:p]; } |
對用戶是否在線狀態(tài)的判斷
| 1 2 | // 取出用戶 XMPPUserCoreDataStorageObject *user = [self.fetchedResultsController objectAtIndexPath:indexPath]; |
?用戶的?user.section?就是用戶的狀態(tài)
// section ?? // 0 在線 ???// 1 離開 ??// 2 離線
?
?
五。用戶注冊:
自己在UI里搭建好注冊頁面,里面需要用戶填寫好用戶信息。在點擊注冊按鈕時,把單例類里自己設(shè)定的一個布爾值isRegisterUser 設(shè)置為YES。 然后重新發(fā)送連接請求。最終還是會來到,連接成功時的代理方法,剛才在這里發(fā)送用戶密碼登錄的,現(xiàn)在可以加一層判斷,如果isRegisterUser的值為YES 就不是發(fā)送用戶密碼登錄了,而是發(fā)送用戶密碼注冊,這里將會用到一個方法registerWithPassword:
| 1 2 3 4 5 6 | if?(self.isRegisterUser) { ????????// 將用戶密碼發(fā)送給服務(wù)器,進(jìn)行用戶注冊 ????????[self.xmppStream registerWithPassword:password error:NULL]; ????????// 將注冊標(biāo)記復(fù)位 ????????self.isRegisterUser =?NO; ????} |
?然后有兩個代理方法,注冊成功和注冊失敗,分別寫上合適的操作。
?
六。添加好友:
搭建一個加好友的UI只需要一個文本框和一個按鈕。
在文本框的回車按鈕點擊代理方法中,做文本框是否為空得判斷,不為空就添加好友,(添加好友方法可以抽出來寫使得結(jié)構(gòu)更加清晰)
添加好友方法如下:有兩個注意點一個是判斷用戶是否寫了域名,如果只是單單寫了個賬號,也可以自動幫他拼接個域名然后注冊。還有個就是判斷是否已經(jīng)是自己的好友,如果是就不做任何操作。如果不是好友 那就馬上添加。最后讓導(dǎo)航控制器返回到登陸界面
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // 添加好友 - (void)addFriendWithName:(NSString?*)name { ????? ????// 你寫了域名那更好,你沒寫系統(tǒng)就自動幫你補(bǔ)上 ????NSRange?range = [name rangeOfString:@"@"]; ????// 如果沒找到 NSNotFound,不要寫0 ????if?(range.location ==?NSNotFound) { ????????name = [name stringByAppendingFormat:@"@%@", [SXXMPPTools sharedXMPPTools].xmppStream.myJID.domain]; ????} ????? ????// 如果已經(jīng)是好友就不需要再次添加 ????XMPPJID *jid = [XMPPJID jidWithString:name]; ????? ????BOOL?contains = [[SXXMPPTools sharedXMPPTools].xmppRosterCoreDataStorage userExistsWithJID:jid xmppStream:[SXXMPPTools sharedXMPPTools].xmppStream]; ????? ????if?(contains) { ????????[[[UIAlertView alloc] initWithTitle:@"提示"?message:@"已經(jīng)是好友,無需添加"?delegate:nil?cancelButtonTitle:@"OK"?otherButtonTitles:nil,?nil] show]; ????????return; ????} ????? ????[[SXXMPPTools sharedXMPPTools].xmppRoster subscribePresenceToUser:jid]; ????? ????[self.navigationController popViewControllerAnimated:YES]; } |
?這里會用到一個通過JID加好友的方法subscribePresenceToUser: 但是這個方法是通過Roster?調(diào)用的所以要在單例類里導(dǎo)入頭文件 聲明屬性,遵守協(xié)議,實現(xiàn)代理方法(董鉑然原創(chuàng))
在單例類里所有特殊類的操作都要寫在xmppStream的懶加載里
| 1 2 3 4 5 6 7 8 | // 實例化 _xmppReconnect = [[XMPPReconnect alloc]init]; _xmppRosterCoreDataStorage = [XMPPRosterCoreDataStorage sharedInstance]; _xmppRoster = [[XMPPRoster alloc]initWithRosterStorage:_xmppRosterCoreDataStorage dispatchQueue:dispatch_get_global_queue(0, 0)];?????? ?// 激活 [_xmppRoster activate:_xmppStream]; // 添加代理 [_xmppRoster addDelegate:self?delegateQueue:dispatch_get_main_queue()];??????????????? |
?
接受到加好友請求的代理方法
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
在這個方法中,先要拼接提示的字符串,就是從?presence.from(申請人的id)的人請求加你為好友。然后設(shè)置彈窗,確定和拒絕,點擊確定按鈕后
| 1 2 | // 接受好友請求 ????????[self.xmppRoster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES]; |
?這個彈窗建議使用iOS8的新功能 ?UIAlertController。 這樣可以不用寫alertDelegate 也能設(shè)置確定按鈕點擊事件 。用?alert addAction: 添加按鈕,把點擊事件寫在block里,最后再取到當(dāng)前窗口的根控制器彈出presentViewController,相當(dāng)于以前的show 。iOS8蘋果的思想漸漸是想把所有彈出控制器的各種方法都慢慢統(tǒng)一到present。
補(bǔ)充:這個功能就是QQ上所謂的加好友不需要驗證,是布爾值可以控制開關(guān)。
| 1 2 | // 取消接收自動訂閱功能,需要確認(rèn)才能夠添加好友! _xmppRoster.autoAcceptKnownPresenceSubscriptionRequests =?NO; |
?
七。好友列表的展示。
這里需要用到查詢結(jié)果調(diào)度器
| 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 29 30 | - (NSFetchedResultsController?*)fetchedResultsController { ????if?(_fetchedResultsController !=?nil) { ????????return??_fetchedResultsController; ????} ????// 指定查詢的實體 ????NSFetchRequest?*request = [[NSFetchRequest?alloc]initWithEntityName:@"XMPPUserCoreDataStorageObject"]; ????? ????// 在線狀態(tài)排序 ????NSSortDescriptor?*sort1 = [NSSortDescriptor?sortDescriptorWithKey:@"sectionNum"?ascending:YES]; ????// 顯示的名稱排序 ????NSSortDescriptor?*sort2 = [NSSortDescriptor?sortDescriptorWithKey:@"displayName"?ascending:YES]; ????? ????// 添加排序 ????request.sortDescriptors = @[sort1,sort2]; ????? ????// 添加謂詞過濾器 ????request.predicate = [NSPredicate?predicateWithFormat:@"!(subscription CONTAINS 'none')"]; ????? ????// 添加上下文 ????NSManagedObjectContext?*ctx = [SXXMPPTools sharedXMPPTools].xmppRosterCoreDataStorage.mainThreadManagedObjectContext; ????? ????// 實例化結(jié)果控制器 ????_fetchedResultsController = [[NSFetchedResultsController?alloc]initWithFetchRequest:request managedObjectContext:ctx sectionNameKeyPath:nil?cacheName:nil]; ????? ????// 設(shè)置他的代理 ????_fetchedResultsController.delegate =?self; ????? ????return?_fetchedResultsController; } |
寫完了結(jié)果調(diào)度器之后要切記在viewdidload頁面首次加載中加上一句,否則不干活
| 1 2 | // 查詢數(shù)據(jù) ????[self.fetchedResultsController performFetch:NULL]; |
?
結(jié)果調(diào)度器有一個代理方法,一旦上下文改變觸發(fā),也就是剛加了好友,或刪除好友時會觸發(fā)
| 1 2 3 4 5 | - (void)controllerDidChangeContent:(NSFetchedResultsController?*)controller { ????NSLog(@"上下文改變"); ????[self.tableView reloadData]; } |
?
整個tableview的數(shù)據(jù)源方法如下
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ????return?self.fetchedResultsController.fetchedObjects.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath?*)indexPath { ????static?NSString?*ID = @"ContactCell"; ????UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; ????? ????XMPPUserCoreDataStorageObject *user = [self.fetchedResultsController objectAtIndexPath:indexPath]; ????? ????// 顯示此好友是否相互關(guān)注 ????NSString?*str = [user.jidStr stringByAppendingFormat:@" | %@",user.subscription]; ????? ????cell.textLabel.text = str ; ????// 這里有個自定義方法傳入section 通過switch判斷返回漢字。section關(guān)系到是否在線 ????cell.detailTextLabel.text = [self?userStatusWithSection:user.section]; ????? ????return?cell; } |
?其中subscription是用戶的好友互加情況
? // 如果是none表示對方還沒有確認(rèn) ??// to 我關(guān)注對方 ?// from 對方關(guān)注我 ?// both 互粉
再提一下?user.section?就是用戶的狀態(tài)
// section ?? // 0 在線 ???// 1 離開 ??// 2 離線
當(dāng)有好友上線,上下文改變時,結(jié)果調(diào)度器會重新排序,然后在線的好友會顯示在上面。
?
八。刪除好友
好友的列表顯示界面可以給tableView添加滑動刪除。(開啟編輯模式)
| 1 2 3 4 5 6 7 8 | #pragma mark - ******************** 開啟編輯模式刪除好友 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath?*)indexPath { ????? ????if?(editingStyle == UITableViewCellEditingStyleDelete) { ????????XMPPUserCoreDataStorageObject *user = [self.fetchedResultsController objectAtIndexPath:indexPath]; ????????? ????????XMPPJID *jid = user.jid; ????????// 接下來是設(shè)置彈窗 |
在彈窗的點擊事件里面刪除好友用到的方法是
| 1 | [[SXXMPPTools sharedXMPPTools].xmppRoster removeUser:jid]; |
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhonghuaxiaodangjia/p/4323505.html
總結(jié)
- 上一篇: HT For Web 拓扑图背景设置
- 下一篇: 如何检查CentOS服务器受到DDOS攻