​XMPPFramework​​是一个OS X/iOS平台的开源项目,使用Objective-C实现了XMPP协议(RFC-3920),同时还提供了用于读写XML的工具,大大简化了基于XMPP的通信应用的开发。

1. 登录和好友上下线

1.1XMPP中常用对象们

  • XMPPStream:xmpp基础服务类
  • XMPPRoster:好友列表类
  • XMPPRosterCoreDataStorage:好友列表(用户账号)在core data中的操作类
  • XMPPvCardCoreDataStorage:好友名片(昵称,签名,性别,年龄等信息)在core data中的操作类
  • XMPPvCardTemp:好友名片实体类,从数据库里取出来的都是它
  • xmppvCardAvatarModule:好友头像
  • XMPPReconnect:如果失去连接,自动重连
  • XMPPRoom:提供多用户聊天支持
  • XMPPPubSub:发布订阅

1.2登录操作,也就是连接xmpp服务器




1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16




​- (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]);​


​}​


​}​


​}​



connect成功之后会依次调用XMPPStreamDelegate的方法,首先调用




1


2


3




​- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket​


 


​...​



然后




1




​- (void)xmppStreamDidConnect:(XMPPStream *)sender​



在该方法下面需要使用xmppStream 的authenticateWithPassword方法进行密码验证,成功的话会响应delegate的方法,就是下面这个




1




​- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender​



1.3上线

实现 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender 委托方法




1


2


3


4




​- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {​


​XMPPPresence *presence = [XMPPPresence presenceWithType:@​​​​"available"​​​​];​


​[self.xmppStream sendElement:presence];​


​}​



1.4退出并断开连接




1


2


3


4


5


6




​- (void)disconnect {​


​XMPPPresence *presence = [XMPPPresence presenceWithType:@​​​​"unavailable"​​​​];​


​[self.xmppStream sendElement:presence];​


 


​[self.xmppStream disconnect];​


​}​



1.5好友状态

获取好友状态,通过实现 




1


2


3




​- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence​


 


​...​



方法,当接收到 presence 标签的内容时,XMPPFramework 框架回调该方法 

一个 presence 标签的格式一般如下:

iOS 的 XMPPFramework 简介_发送消息

presence 的状态:

  • available 上线
  • away 离开
  • do not disturb 忙碌
  • unavailable 下线




1


2


3


4


5


6


7


8


9


10


11




​- (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"​​​​]) {​


​//​


​}​


​}​


​}​



2. 接收消息和发送消息

2.1接收消息

通过实现 




1




​- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message;​



方法

当接收到 message 标签的内容时,XMPPFramework 框架回调该方法

根据 XMPP 协议,消息体的内容存储在标签 body 内




1


2


3




​- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {​


​NSString *messageBody = [[message elementForName:@​​​​"body"​​​​] stringValue];​


​}​



2.2发送消息

发送消息,我们需要根据 XMPP 协议,将数据放到标签内,例如:

iOS 的 XMPPFramework 简介_发送消息_02




1


2


3


4


5


6


7


8


9


10




​- (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];​


​}​



3. 获取好友信息和删除好友

3.1好友列表和好友名片




1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17




​[_xmppRoster fetchRoster];​​​​//获取好友列表​


​//获取到一个好友节点​


​- (void)xmppRoster:(XMPPRoster *)sender didRecieveRosterItem:(NSXMLElement *)item​


​//获取完好友列表​


​- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender​


​//到服务器上请求联系人名片信息​


​- (void)fetchvCardTempForJID:(XMPPJID *)jid;​


​//请求联系人的名片,如果数据库有就不请求,没有就发送名片请求​


​- (void)fetchvCardTempForJID:(XMPPJID *)jid ignoreStorage:(BOOL)ignoreStorage;​


​//获取联系人的名片,如果数据库有就返回,没有返回空,并到服务器上抓取​


​- (XMPPvCardTemp *)vCardTempForJID:(XMPPJID *)jid shouldFetch:(BOOL)shouldFetch;​


​//更新自己的名片信息​


​- (void)updateMyvCardTemp:(XMPPvCardTemp *)vCardTemp;​


​//获取到一盒联系人的名片信息的回调​


​- (void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule ​


​didReceivevCardTemp:(XMPPvCardTemp *)vCardTemp ​


​forJID:(XMPPJID *)jid​



3.2添加好友




1


2


3


4


5


6


7


8


9




​//name为用户账号​


​- (void)XMPPAddFriendSubscribe:(NSString *)name​


​{​


​//XMPPHOST 就是服务器名,  主机名​


​XMPPJID *jid = [XMPPJID jidWithString:[NSString stringWithFormat:@​​​​"%@@%@"​​​​,name,XMPPHOST]];​


​//[presence addAttributeWithName:@"subscription" stringValue:@"好友"];​


​[xmppRoster subscribePresenceToUser:jid];​


 


​}​



3.3收到添加好友的请求




1


2


3


4


5


6


7


8


9


10


11


12


13


14


15




​- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence​


​{​


​//取得好友状态​


​NSString *presenceType = [NSString stringWithFormat:@​​​​"%@"​​​​, [presence type]]; ​​​​//online/offline​


​//请求的用户​


​NSString *presenceFromUser =[NSString stringWithFormat:@​​​​"%@"​​​​, [[presence from] user]];​


​NSLog(@​​​​"presenceType:%@"​​​​,presenceType);​


 


​NSLog(@​​​​"presence2:%@  sender2:%@"​​​​,presence,sender);​


 


​XMPPJID *jid = [XMPPJID jidWithString:presenceFromUser];​


​//接收添加好友请求​


​[xmppRoster acceptPresenceSubscriptionRequestFrom:jid andAddToRoster:YES];​


 


​}​



3.4删除好友




1


2


3


4


5


6


7




​//删除好友,name为好友账号​


​- (void)removeBuddy:(NSString *)name  ​


​{  ​


​XMPPJID *jid = [XMPPJID jidWithString:[NSString stringWithFormat:@​​​​"%@@%@"​​​​,name,XMPPHOST]];  ​


 


​[self xmppRoster] removeUser:jid];  ​


​}​



4. 聊天室

初始化聊天室




1


2


3


4


5


6




​XMPPJID *roomJID = [XMPPJID jidWithString:ROOM_JID];​


 


​xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:self jid:roomJID];​


 


​[xmppRoom activate:xmppStream];​


​[xmppRoom addDelegate:self delegateQueue:dispatch_get_main_queue()];​



创建聊天室成功




1


2


3


4




​- (void)xmppRoomDidCreate:(XMPPRoom *)sender​


​{​


​DDLogInfo(@​​​​"%@: %@"​​​​, THIS_FILE, THIS_METHOD);​


​}​



加入聊天室,使用昵称




1




​[xmppRoom joinRoomUsingNickname:@​​​​"quack"​​ ​​history:nil];​



获取聊天室信息




1


2


3


4


5


6


7




​- (void)xmppRoomDidJoin:(XMPPRoom *)sender​


​{​


​[xmppRoom fetchConfigurationForm];​


​[xmppRoom fetchBanList];​


​[xmppRoom fetchMembersList];​


​[xmppRoom fetchModeratorsList];​


​}​



如果房间存在,会调用委托




1


2


3


4


5


6




​// 收到禁止名单列表​


​- (void)xmppRoom:(XMPPRoom *)sender didFetchBanList:(NSArray *)items;​


​// 收到好友名单列表​


​- (void)xmppRoom:(XMPPRoom *)sender didFetchMembersList:(NSArray *)items;​


​// 收到主持人名单列表​


​- (void)xmppRoom:(XMPPRoom *)sender didFetchModeratorsList:(NSArray *)items;​



房间不存在,调用委托




1


2


3




​- (void)xmppRoom:(XMPPRoom *)sender didNotFetchBanList:(XMPPIQ *)iqError;​


​- (void)xmppRoom:(XMPPRoom *)sender didNotFetchMembersList:(XMPPIQ *)iqError;​


​- (void)xmppRoom:(XMPPRoom *)sender didNotFetchModeratorsList:(XMPPIQ *)iqError;​



离开房间




1




​[xmppRoom deactivate:xmppStream];​



XMPPRoomDelegate的其他代理方法:

离开聊天室




1


2


3


4




​- (void)xmppRoomDidLeave:(XMPPRoom *)sender​


​{​


​DDLogVerbose(@​​​​"%@: %@"​​​​, THIS_FILE, THIS_METHOD);​


​}​



新人加入群聊




1


2


3


4




​- (void)xmppRoom:(XMPPRoom *)sender occupantDidJoin:(XMPPJID *)occupantJID​


​{​


​DDLogVerbose(@​​​​"%@: %@"​​​​, THIS_FILE, THIS_METHOD);​


​}​



有人退出群聊




1


2


3


4




​- (void)xmppRoom:(XMPPRoom *)sender occupantDidLeave:(XMPPJID *)occupantJID​


​{​


​DDLogVerbose(@​​​​"%@: %@"​​​​, THIS_FILE, THIS_METHOD);​


​}​



有人在群里发言




1


2


3


4




​- (void)xmppRoom:(XMPPRoom *)sender didReceiveMessage:(XMPPMessage *)message fromOccupant:(XMPPJID *)occupantJID​


​{​


​DDLogVerbose(@​​​​"%@: %@"​​​​, THIS_FILE, THIS_METHOD);​


​}​



5. 消息回执

这个是XEP-0184协议的内容

协议内容:

发送消息时附加回执请求

iOS 的 XMPPFramework 简介_发送消息_03

代码实现




1


2


3


4


5


6


7




​NSString *siID = [XMPPStream generateUUID];​


​//发送消息​


​XMPPMessage *message = [XMPPMessage messageWithType:@​​​​"chat"​​ ​​to:jid elementID:siID];​


​NSXMLElement *receipt = [NSXMLElement elementWithName:@​​​​"request"​​ ​​xmlns:@​​​​"urn:xmpp:receipts"​​​​];​


​[message addChild:receipt];​


​[message addBody:@​​​​"测试"​​​​];​


​[self.xmppStream sendElement:message];​



收到回执请求的消息,发送回执

iOS 的 XMPPFramework 简介_好友列表_04

代码实现




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


31


32




​- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message​


​{​


​//回执判断​


​NSXMLElement *request = [message elementForName:@​​​​"request"​​​​];​


​if​​ ​​(request)​


​{​


​if​​ ​​([request.xmlns isEqualToString:@​​​​"urn:xmpp:receipts"​​​​])​​​​//消息回执​


​{​


​//组装消息回执​


​XMPPMessage *msg = [XMPPMessage messageWithType:[message attributeStringValueForName:@​​​​"type"​​​​] to:message.from elementID:[message attributeStringValueForName:@​​​​"id"​​​​]];​


​NSXMLElement *recieved = [NSXMLElement elementWithName:@​​​​"received"​​ ​​xmlns:@​​​​"urn:xmpp:receipts"​​​​];​


​[msg addChild:recieved];​


 


​//发送回执​


​[self.xmppStream sendElement:msg];​


​}​


​}​​​​else​


​{​


​NSXMLElement *received = [message elementForName:@​​​​"received"​​​​];​


​if​​ ​​(received)​


​{​


​if​​ ​​([received.xmlns isEqualToString:@​​​​"urn:xmpp:receipts"​​​​])​​​​//消息回执​


​{​


​//发送成功​


​NSLog(@​​​​"message send success!"​​​​);​


​}  ​


​}  ​


​}  ​


 


​//消息处理  ​


​//...  ​


​}​



6. 添加AutoPing

为了监听服务器是否有效,增加心跳监听。用XEP-0199协议,在XMPPFrameWork框架下,封装了 XMPPAutoPing 和 XMPPPing两个类都可以使用,因为XMPPAutoPing已经组合进了XMPPPing类,所以XMPPAutoPing使用起来更方便。




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


31




​//初始化并启动ping​


​-(void)autoPingProxyServer:(NSString*)strProxyServer​


​{​


​_xmppAutoPing = [[XMPPAutoPingalloc] init];​


​[_xmppAutoPingactivate:_xmppStream];​


​[_xmppAutoPingaddDelegate:selfdelegateQueue:  dispatch_get_main_queue()];​


​_xmppAutoPing.respondsToQueries = YES;​


​_xmppAutoPing.pingInterval=2;​​​​//ping 间隔时间​


​if​​ ​​(nil != strProxyServer)​


​{​


​_xmppAutoPing.targetJID = [XMPPJID jidWithString: strProxyServer ];​​​​//设置ping目标服务器,如果为nil,则监听socketstream当前连接上的那个服务器​


​}​


​}​


​//卸载监听​


​[_xmppAutoPing   deactivate];​


​[_xmppAutoPing   removeDelegate:self];​


​_xmppAutoPing = nil;​


​//ping XMPPAutoPingDelegate的委托方法:​


​- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender​


​{​


​NSLog(@​​​​"- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender"​​​​);​


​}​


​- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender​


​{​


​NSLog(@​​​​"- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender"​​​​);​


​}​


 


​- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender​


​{​


​NSLog(@​​​​"- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender"​​​​);​


​}​