XMPPFramework
GitHub: https://github.com/robbiehanson/XMPPFramework
獲取源代碼
git clone https://github.com/robbiehanson/XMPPFramework.git
checkout XMPPFramework 一個最新的 branch
添加依賴
> 拷貝
> 同樣的步驟,拷貝 CocoaAsyncSocket 和 KissXML 並添加到項目中
CocoaAsyncSocket 依賴 CFNetwork.framework 和 Security.framework,在 TARGETS -> Build Phases -> Link Binary With Libraries 添加
KissXML 使用了 libxml2 解析 XML,所以
首先,我們需要在 TARGETS -> Build Phases -> Link Binary With Libraries 添加 libXML2.dylib
然後,在 TARGETS -> Build Settings -> Other Linker Flags 添加 -lxml2,TARGETS -> Build Settings -> Header Search Paths 添加 /usr/include/libxml2
> 拷貝
添加 XMPPFramework
拷貝源碼目錄下的 Authentication Categories Core 和 Utilities 到項目根目錄下並添加到項目中
此外,需要添加動態連接庫 libresolv.dylib ,在 TARGETS -> Build Phases -> Link Binary With Libraries 添加
添加擴展
你可以根據自己的需要,添加
你可能遇到的問題:
> "XMPPFramework.h" file not found
XMPPFramework.h 內容如下,可根據實際使用模塊進行刪改:
#import "XMPP.h" // List the modules you're using here. #import "XMPPReconnect.h" #import "XMPPRoster.h" #import "XMPPRosterCoreDataStorage.h" #import "XMPPvCardTempModule.h" #import "XMPPvCardAvatarModule.h" #import "XMPPvCardCoreDataStorage.h" #import "XMPPCapabilities.h" #import "XMPPCapabilitiesCoreDataStorage.h" #import "XMPPMUC.h" #import "XMPPRoomCoreDataStorage.h"
還有一些問題,是由於沒有添加Extension需要的依賴庫所產生的
可在 TARGETS -> Build Phases -> Link Binary With Libraries 添加
CoreData.framework SystemConfiguration.framework CoreLocation.framework
ARC 警告
XMPPFramework 使用 ARC,如果你的項目沒有使用 ARC,build 之後你會得到許許多多的 ARC 警告。
不要忽視這些警告,它會導致你的程序因 memory leak 而崩潰……
Edit -> Refactor -> convert to Objective-C ARC,消滅這些警告!
參考: https://github.com/robbiehanson/XMPPFramework/wiki/GettingStarted_iOS
用戶登錄
准備工作
比較知名的開源XMPP服務器:一個是Openfire,一個是ejabberd
Openfire 使用 Java 語言編寫,比較容易上手,地址:http://www.igniterealtime.org/projects/openfire/
ejabberd 使用 Erlang 語言編寫,是一款非常知名的 Erlang 開源項目,地址:http://www.ejabberd.im/
安裝 ejabberd,可以參考我的博客:【ejabberd】安裝XMPP服務器ejabberd(Ubuntu 12.04)
搭建一個自己的 XMPP 服務器之後,就讓我們開始吧!
連接服務器
1、新建一個 XMPPStream 對象,添加委托
添加委托方法 - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
參數 delegateQueue 為委托回調所使用的 GCD 隊列,dispatch_get_main_queue() 獲取主線程 GCD 隊列
2、設置 JID 和 主機名
JID 一般由三部分構成:用戶名,域名和資源名,例如 [email protected]/Anthony
如果沒有設置主機名,則使用 JID 的域名作為主機名
端口號是可選的,默認是 5222
3、連接
- (void)connect { if (self.xmppStream == nil) { self.xmppStream = [[XMPPStream alloc] init]; [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; } if (![self.xmppStream isConnected]) { NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:@"username"]; XMPPJID *jid = [XMPPJID jidWithUser:username domain:@"lizhen" resource:@"Ework"]; [self.xmppStream setMyJID:jid]; [self.xmppStream setHostName:@"10.4.125.113"]; NSError *error = nil; if (![self.xmppStream connect:&error]) { NSLog(@"Connect Error: %@", [[error userInfo] description]); } } }
身份認證
實現 - (void)xmppStreamDidConnect:(XMPPStream *)sender 委托方法
連接服務器成功後,回調該方法
This method is called after the XML stream has been fully opened. More precisely, this method is called after an opening
身份認證方法 - (BOOL)authenticateWithPassword:(NSString *)inPassword error:(NSError **)errPtr
- (void)xmppStreamDidConnect:(XMPPStream *)sender { NSString *password = [[NSUserDefaults standardUserDefaults] objectForKey:@"password"]; NSError *error = nil; if (![self.xmppStream authenticateWithPassword:password error:&error]) { NSLog(@"Authenticate Error: %@", [[error userInfo] description]); } }
上線
實現 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender 委托方法
身份認證成功後,回調該方法
This method is called after authentication has successfully finished.
If authentication fails for some reason, the xmppStream:didNotAuthenticate: method will be called instead.
新建一個 XMPPPresence 對象,類型為 available,發送!
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"]; [self.xmppStream sendElement:presence]; }
退出並斷開連接
新建一個 XMPPPresence 對象,類型為 unavailable,發送!
斷開連接
- (void)disconnect { XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"]; [self.xmppStream sendElement:presence]; [self.xmppStream disconnect]; }
好友狀態
獲取好友狀態,通過實現
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
方法
當接收到
標簽的內容時,XMPPFramework 框架回調該方法
一個
標簽的格式一般如下:
這裡是顯示的內容
這裡是顯示的狀態 內presence 的狀態:
available 上線
away 離開
do not disturb 忙碌
unavailable 下線
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence { NSString *presenceType = [presence type]; NSString *presenceFromUser = [[presence from] user]; if (![presenceFromUser isEqualToString:[[sender myJID] user]]) { if ([presenceType isEqualToString:@"available"]) { // } else if ([presenceType isEqualToString:@"unavailable"]) { // } } }
接收消息
通過實現
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message;
方法
當接收到
標簽的內容時,XMPPFramework 框架回調該方法 根據 XMPP 協議,消息體的內容存儲在標簽
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message { NSString *messageBody = [[message elementForName:@"body"] stringValue]; }
發送消息
發送消息,我們需要根據 XMPP 協議,將數據放到
標簽內,例如:
Hello World!
- (void)sendMessage:(NSString *) message toUser:(NSString *) user { NSXMLElement *body = [NSXMLElement elementWithName:@"body"]; [body setStringValue:message]; NSXMLElement *message = [NSXMLElement elementWithName:@"message"]; [message addAttributeWithName:@"type" stringValue:@"chat"]; NSString *to = [NSString stringWithFormat:@"%@@example.com", user]; [message addAttributeWithName:@"to" stringValue:to]; [message addChild:body]; [self.xmppStream sendElement:message]; }
好友列表
好友列表,在 XMPP 中被稱為 roster,花名冊?
獲取 roster 需要客戶端發送
標簽向 XMPP 服務器端查詢
一個 IQ 請求:
from="[email protected]"
to="example.com"
id="1234567">
type 屬性,說明了該 iq 的類型為 get,與 HTTP 類似,向服務器端請求信息
from 屬性,消息來源,這裡是你的 JID
to 屬性,消息目標,這裡是服務器域名
id 屬性,標記該請求 ID,當服務器處理完畢請求 get 類型的 iq 後,響應的 result 類型 iq 的 ID 與 請求 iq 的 ID 相同
子標簽,說明了客戶端需要查詢 roster
- (void)queryRoster { NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; XMPPJID *myJID = self.xmppStream.myJID; [iq addAttributeWithName:@"from" stringValue:myJID.description]; [iq addAttributeWithName:@"to" stringValue:myJID.domain]; [iq addAttributeWithName:@"id" stringValue:[self generateID]]; [iq addAttributeWithName:@"type" stringValue:@"get"]; [iq addChild:query]; [self.xmppStream sendElement:iq]; }
一個 IQ 響應:
id="1234567"
to="[email protected]">
type 屬性,說明了該 iq 的類型為 result,查詢的結果
標簽的子標簽 - ,為查詢的子項,即為 roster
item 標簽的屬性,包含好友的 JID,和其它可選的屬性,例如昵稱等。
通過實現
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq;
方法
當接收到
標簽的內容時,XMPPFramework 框架回調該方法 - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq { if ([@"result" isEqualToString:iq.type]) { NSXMLElement *query = iq.childElement; if ([@"query" isEqualToString:query.name]) { NSArray *items = [query children]; for (NSXMLElement *item in items) { NSString *jid = [item attributeStringValueForName:@"jid"]; XMPPJID *xmppJID = [XMPPJID jidWithString:jid]; [self.roster addObject:xmppJID]; } } } }