本文主要是介绍iOS即时通信之XMPP框架的使用及原理简介,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
小公司可以考虑xmpp框架,xmpp内部封装好了异步的socket通信
XMPP是基于XML协议的分散型通讯网络,只要服务器的协议相同,服务器就可以和服务器通信;
XMPP的核心就是XML流传传输协议;
XMPP是C/S 架构;
XMPP中对应的模块会吧数据从服务器拿出来,然后放到本地数据库,我们开发只需要获取本地服务器的数据.
XMPP----------可扩展通讯和标示协议
==================
连接服务器,登录,注册
// 1. 初始化XMPPStream
-(void)setupXMPPStream;
// 2.连接到服务器
-(void)connectToHost;
// 3.连接到服务成功后,再发送密码授权
-(void)sendPwdToHost;
// 4.授权成功后,发送"在线"消息
-(void)sendOnlineToHost;
@end
@implementation WCXMPPTool
singleton_implementation(WCXMPPTool)
#pragma mark -私有方法
#pragma mark 初始化XMPPStream
-(void)setupXMPPStream{
_xmppStream = [[XMPPStreamalloc]init];
#warning 每一个模块添加后都要激活
//添加电子名片模块
_vCardStorage = [XMPPvCardCoreDataStoragesharedInstance];
_vCard = [[XMPPvCardTempModulealloc]initWithvCardStorage:_vCardStorage];
//激活
[_vCard activate:_xmppStream];
//添加头像模块
_avatar = [[XMPPvCardAvatarModulealloc]initWithvCardTempModule:_vCard];
[_avatar activate:_xmppStream];
// 设置代理
[_xmppStreamaddDelegate:selfdelegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)];
}
#pragma mark 连接到服务器
-(void)connectToHost{
WCLog(@"开始连接到服务器");
if (!_xmppStream) {
[selfsetupXMPPStream];
}
// 设置登录用户JID
//resource 标识用户登录的客户端 iphone android
// 从单例获取用户名
NSString *user = nil;
if (self.isRegisterOperation) {
user = [WCUserInfo sharedWCUserInfo].registerUser;
}else{
user = [WCUserInfo sharedWCUserInfo].user;
}
XMPPJID *myJID = [XMPPJIDjidWithUser:userdomain:@"teacher.local"resource:@"iphone" ];
_xmppStream.myJID = myJID;
// 设置服务器域名
_xmppStream.hostName =@"teacher.local";//不仅可以是域名,还可是IP地址
// 设置端口如果服务器端口是5222,可以省略
_xmppStream.hostPort =5222;
// 连接
NSError *err = nil;
if(![_xmppStreamconnectWithTimeout:XMPPStreamTimeoutNoneerror:&err]){
WCLog(@"%@",err);
}
}
#pragma mark 连接到服务成功后,再发送密码授权
-(void)sendPwdToHost{
WCLog(@"再发送密码授权");
NSError *err = nil;
// 从单例里获取密码
NSString *pwd = [WCUserInfosharedWCUserInfo].pwd;
[_xmppStreamauthenticateWithPassword:pwderror:&err];
if (err) {
WCLog(@"%@",err);
}
}
#pragma mark 授权成功后,发送"在线"消息
-(void)sendOnlineToHost{
WCLog(@"发送在线消息");
XMPPPresence *presence = [XMPPPresencepresence];
WCLog(@"%@",presence);
[_xmppStream sendElement:presence];
}
#pragma mark -XMPPStream的代理
#pragma mark 与主机连接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
WCLog(@"与主机连接成功");
if (self.isRegisterOperation) {//注册操作,发送注册的密码
NSString *pwd = [WCUserInfosharedWCUserInfo].registerPwd;
[_xmppStreamregisterWithPassword:pwderror:nil];
}else{//登录操作
// 主机连接成功后,发送密码进行授权
[selfsendPwdToHost];
}
}
#pragma mark 与主机断开连接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
// 如果有错误,代表连接失败
// 如果没有错误,表示正常的断开连接(人为断开连接)
if(error && _resultBlock){
_resultBlock(XMPPResultTypeNetErr);
}
WCLog(@"与主机断开连接 %@",error);
}
#pragma mark 授权成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
WCLog(@"授权成功");
[selfsendOnlineToHost];
// 回调控制器登录成功
if(_resultBlock){
_resultBlock(XMPPResultTypeLoginSuccess);
}
}
#pragma mark 授权失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
WCLog(@"授权失败 %@",error);
// 判断block有无值,回调给登录控制器
if (_resultBlock) {
_resultBlock(XMPPResultTypeLoginFailure);
}
}
#pragma mark 注册成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender{
WCLog(@"注册成功");
if(_resultBlock){
_resultBlock(XMPPResultTypeRegisterSuccess);
}
}
#pragma mark 注册失败
-(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{
WCLog(@"注册失败 %@",error);
if(_resultBlock){
_resultBlock(XMPPResultTypeRegisterFailure);
}
}
#pragma mark -公共方法
-(void)xmppUserlogout{
// 1." 发送 "离线"消息"
XMPPPresence *offline = [XMPPPresencepresenceWithType:@"unavailable"];
[_xmppStream sendElement:offline];
// 2. 与服务器断开连接
[_xmppStream disconnect];
// 3. 回到登录界面
// UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Login" bundle:nil];
//
// self.window.rootViewController = storyboard.instantiateInitialViewController;
[UIStoryboardshowInitialVCWithName:@"Login"];
//4.更新用户的登录状态
[WCUserInfosharedWCUserInfo].loginStatus =NO;
[[WCUserInfosharedWCUserInfo]saveUserInfoToSanbox];
}
-(void)xmppUserLogin:(XMPPResultBlock)resultBlock{
// 先把block存起来
_resultBlock = resultBlock;
// Domain=XMPPStreamErrorDomain Code=1 "Attempting to connect while already connected or connecting." UserInfo=0x7fd86bf06700 {NSLocalizedDescription=Attempting to connect while already connected or connecting.}
// 如果以前连接过服务,要断开
[_xmppStream disconnect];
// 连接主机成功后发送登录密码
[selfconnectToHost];
}
-(void)xmppUserRegister:(XMPPResultBlock)resultBlock{
// 先把block存起来
_resultBlock = resultBlock;
// 如果以前连接过服务,要断开
[_xmppStream disconnect];
// 连接主机成功后发送注册密码
[selfconnectToHost];
}
xmpp的一般开发步骤:
1.导入指定模块的头文件
2.创建导入的指定模块的对象
3.设置参数
4.设置代理 (需要代理才设置)
5.激活模块
=================================
0054电子明片(个人资料)的设置:
个人资料功能模块------XMPPVcardTempModule.h
个人资料的存储集合----------XMPPvCardTemp *myvCardTemp,是XMPPVcardTempModule的一个属性;
个人资料的数据库存储-------XMPPvCardCoreDataStorage.h
个人资料数据库对象-----------XMPPvCardTempCoreDataStorageObject.h
头像功能模块-----XMPPvCardAVatarModule.h
头像数据存储对象-----XMPPvCardAvatarCoreDataStorageObject.h
@property (nonatomic, strong)XMPPvCardCoreDataStorage *vCardStorage;//电子名片的数据存储
@property (nonatomic, strong)XMPPvCardAvatarModule *avatar;//头像模块
@property (nonatomic, strong)XMPPvCardTempModule *vCard;//电子名片模块
_xmppStream = [[XMPPStreamalloc]init];
#warning 每一个模块添加后都要激活
//添加电子名片模块
_vCardStorage = [XMPPvCardCoreDataStoragesharedInstance];
_vCard = [[XMPPvCardTempModulealloc]initWithvCardStorage:_vCardStorage];
//激活
[_vCard activate:_xmppStream];
//添加头像模块
_avatar = [[XMPPvCardAvatarModulealloc]initWithvCardTempModule:_vCard];
[_avatar activate:_xmppStream];
//xmpp提供了一个方法,直接获取个人信息(电子面片的功能模块有一个个人信息的集合属性:XMPPvCardTemp *myvCardTemp)
XMPPvCardTemp *myVCard =vCard.myvCardTemp;
// 设置头像
if(myVCard.photo){
self.headerView.image = [UIImageimageWithData:myVCard.photo];
}
// 设置昵称
self.nickNameLabel.text = myVCard.nickname;
// 设置微信号[用户名]
NSString *user = [WCUserInfosharedWCUserInfo].user;
self.weixinNumLabel.text = [NSStringstringWithFormat:@"微信号:%@",user];
//更新这个方法内部会实现数据上传到服务器,无需程序自己操作
[vCardupdateMyvCardTemp:myvCard];
@property (nonatomic, strong)
XMPPReconnect *reconnect;// 自动连接模块
//添加自动连接模块
_reconnect = [[XMPPReconnectalloc]init];
[_reconnect activate:_xmppStream];
@property (nonatomic, strong)XMPPRosterCoreDataStorage *rosterStorage;//花名册数据存储
@property (nonatomic, strong)XMPPRoster *roster;//花名册模块
//添加花名册模块【获取好友列表】
_rosterStorage = [[XMPPRosterCoreDataStoragealloc]init];
_roster = [[XMPPRosteralloc]initWithRosterStorage:_rosterStorage];
[_roster activate:_xmppStream];
//方法一: 通过上下文 获取好友数据
// 1.获取对象管理上下文【关联到数据库XMPPRoster.sqlite】
NSManagedObjectContext *context =rosterStorage.mainThreadManagedObjectContext;
// 2.FetchRequest【查哪张表】通过实体(相当于表)查找
NSFetchRequest *request = [NSFetchRequestfetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];
// 3.设置过滤和排序
// 过滤当前登录用户的好友
NSString *jid = [WCUserInfosharedWCUserInfo].jid;
NSPredicate *pre = [NSPredicatepredicateWithFormat:@"streamBareJidStr = %@",jid];
request.predicate = pre;
//排序
NSSortDescriptor *sort = [NSSortDescriptorsortDescriptorWithKey:@"displayName"ascending:YES];
request.sortDescriptors = @[sort];
// 4.执行请求获取数据集合(集合里面的元素都是XMPPUserCoreDataStorageObject类型)
self.friends = [contextexecuteFetchRequest:requesterror:nil];
NSLog(@"%@",self.friends);
<NSFetchedResultsControllerDelegate>协议
{
NSFetchedResultsController *_resultsContrl;
}
// 1.上下文【关联到数据库XMPPRoster.sqlite】
NSManagedObjectContext *context =rosterStorage.mainThreadManagedObjectContext;
// 2.FetchRequest【查哪张表】
NSFetchRequest *request = [NSFetchRequestfetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];
// 3.设置过滤和排序
// 过滤当前登录用户的好友
NSString *jid = [WCUserInfosharedWCUserInfo].jid;
NSPredicate *pre = [NSPredicatepredicateWithFormat:@"streamBareJidStr = %@",jid];
request.predicate = pre;
//排序
NSSortDescriptor *sort = [NSSortDescriptorsortDescriptorWithKey:@"displayName"ascending:YES];
request.sortDescriptors = @[sort];
// 4.执行请求获取数据,获取好友集合(集合里面的元素都是XMPPUserCoreDataStorageObject类型)
_resultsContrl = [[NSFetchedResultsControlleralloc]initWithFetchRequest:requestmanagedObjectContext:contextsectionNameKeyPath:nilcacheName:nil];
_resultsContrl.delegate =self;
NSError *err = nil;
[_resultsContrlperformFetch:&err];
#pragma mark 当数据的内容发生改变后,会调用这个方法
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
WCLog(@"数据发生改变");
//刷新表格
[self.tableViewreloadData];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return_resultsContrl.fetchedObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
staticNSString *ID =@"ContactCell";
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:ID];
// 获取对应好友
//XMPPUserCoreDataStorageObject *friend =self.friends[indexPath.row];
XMPPUserCoreDataStorageObject *friend = _resultsContrl.fetchedObjects[indexPath.row];//通过传控制器获取的好友实体对象
// sectionNum
// “0”- 在线
// “1”- 离开
// “2”- 离线
switch ([friend.sectionNumintValue]) {//好友对象的状态
case 0:
cell.detailTextLabel.text =@"在线";
break;
case 1:
cell.detailTextLabel.text =@"离开";
break;
case 2:
cell.detailTextLabel.text =@"离线";
break;
default:
break;
}
cell.textLabel.text = friend.jidStr;//jid的类型是xxx@aaa.com
return cell;
}
openfire服务器删除好友后,下面这个方法会接收到删除好友的消息后,花名册模块就会吧数据库roster.sqlite相应的好友删除
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller,
// 删除好友, 实现这个方法,cell往左滑就会有个delete
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if (editingStyle ==UITableViewCellEditingStyleDelete) {
WCLog(@"删除好友");
XMPPUserCoreDataStorageObject *friend =_resultsContrl.fetchedObjects[indexPath.row];
XMPPJID *freindJid = friend.jid;
[[WCXMPPTool sharedWCXMPPTool].roster removeUser:freindJid];
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
// 添加好友
// 1.获取好友账号
NSString *user = textField.text;
WCLog(@"%@",user);
// 判断这个账号是否为手机号码
if(![textField isTelphoneNum]){
//提示
[self showAlert:@"请输入正确的手机号码"];
return YES;
}
//判断是否添加自己
if([user isEqualToString:[WCUserInfosharedWCUserInfo].user]){
[self showAlert:@"不能添加自己为好友"];
return YES;
}
NSString *jidStr = [NSStringstringWithFormat:@"%@@%@",user,domain];
XMPPJID *friendJid = [XMPPJIDjidWithString:jidStr];
//判断好友是否已经存在
if([[WCXMPPToolsharedWCXMPPTool].rosterStorageuserExistsWithJID:friendJidxmppStream:[WCXMPPToolsharedWCXMPPTool].xmppStream]){
[self showAlert:@"当前好友已经存在"];
return YES;
}
// 2.发送好友添加的请求
// 添加好友,xmpp有个叫订阅
[[WCXMPPToolsharedWCXMPPTool].rostersubscribePresenceToUser:friendJid];
return YES;
}
-(void)showAlert:(NSString *)msg{
UIAlertView *alert = [[UIAlertViewalloc]initWithTitle:@"温馨提示"message:msgdelegate:nilcancelButtonTitle:@"谢谢"otherButtonTitles:nil,nil];
[alert show];
}
@end
// 消息模块
消息功能模块-------"XMPPMessageArchiving.h"
消息数据存储------"XMPPMessageArchivingCoreDataStorage.h"
消息实体对象-------XMPPMessageArchiving_Message_CoreDataObject.h
最近联系人实体对象------XMPPMessageArchiving_Contact_CoreDataObject.h
#pragma mark 加载XMPPMessageArchiving数据库的数据显示在表格
-(void)loadMsgs{
// 上下文
NSManagedObjectContext *context = [WCXMPPToolsharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext;
// 请求对象
NSFetchRequest *request = [NSFetchRequestfetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
// 过滤、排序
// 1.当前登录用户的JID的消息
// 2.好友的Jid的消息
NSPredicate *pre = [NSPredicatepredicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfosharedWCUserInfo].jid,self.friendJid.bare];
NSLog(@"%@",pre);
request.predicate = pre;
// 时间升序
NSSortDescriptor *timeSort = [NSSortDescriptorsortDescriptorWithKey:@"timestamp"ascending:YES];
request.sortDescriptors = @[timeSort];
// 查询
_resultsContr = [[NSFetchedResultsControlleralloc]initWithFetchRequest:requestmanagedObjectContext:contextsectionNameKeyPath:nilcacheName:nil];
NSError *err = nil;
// 代理
_resultsContr.delegate =self;
[_resultsContr performFetch:&err];
NSLog(@"%@",_resultsContr.fetchedObjects);
if (err) {
WCLog(@"%@",err);
}
}
#pragma mark -表格的数据源
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return_resultsContr.fetchedObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID =@"ChatCell";
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCellalloc]initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:ID];
}
// 获取聊天消息对象
XMPPMessageArchiving_Message_CoreDataObject *msg = _resultsContr.fetchedObjects[indexPath.row];
// 判断是图片还是纯文本
NSString *chatType = [msg.messageattributeStringValueForName:@"bodyType"];
if ([chatType isEqualToString:@"image"]) {
//下图片显示
[cell.imageViewsd_setImageWithURL:[NSURLURLWithString:msg.body]placeholderImage:[UIImageimageNamed:@"DefaultProfileHead_qq"]];
cell.textLabel.text =nil;
}else if([chatTypeisEqualToString:@"text"]){
//显示消息
if ([msg.outgoingboolValue]) {//自己发
cell.textLabel.text = msg.body;
}else{//别人发的
cell.textLabel.text = msg.body;
}
cell.imageView.image =nil;
}
// //显示消息
// if ([msg.outgoing boolValue]) {//自己发
// cell.textLabel.text = [NSString stringWithFormat:@"Me: %@",msg.body];
// }else{//别人发的
// cell.textLabel.text = [NSString stringWithFormat:@"Other: %@",msg.body];
// }
return cell;
}
#pragma mark ResultController的代理
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
// 刷新数据
[self.tableViewreloadData];
[selfscrollToTableBottom];
}
#pragma mark TextView的代理
-(void)textViewDidChange:(UITextView *)textView{
//获取ContentSize
CGFloat contentH = textView.contentSize.height;
NSLog(@"textView的content的高度 %f",contentH);
// 大于33,超过一行的高度/小于68高度是在三行内
if (contentH > 33 && contentH < 68 ) {
self.inputViewHeightConstraint.constant = contentH +18;
}
NSString *text = textView.text;
// 换行就等于点击了的send
if ([text rangeOfString:@"\n"].length !=0) {
NSLog(@"发送数据 %@",text);
// 去除换行字符
text = [text stringByTrimmingCharactersInSet:[NSCharacterSetwhitespaceAndNewlineCharacterSet]];
[selfsendMsgWithText:textbodyType:@"text"];
//清空数据
textView.text = nil;
// 发送完消息 把inputView的高度改回来
self.inputViewHeightConstraint.constant =50;
}else{
NSLog(@"%@",textView.text);
}
}
#pragma mark 发送聊天消息
-(void)sendMsgWithText:(NSString *)text bodyType:(NSString *)bodyType{
XMPPMessage *msg = [XMPPMessagemessageWithType:@"chat"to:self.friendJid];
//text 纯文本
//image 图片
[msg addAttributeWithName:@"bodyType"stringValue:bodyType];
// 设置内容
[msg addBody:text];
NSLog(@"%@",msg);
[[WCXMPPToolsharedWCXMPPTool].xmppStreamsendElement:msg];
}
#pragma mark 滚动到底部
-(void)scrollToTableBottom{
NSInteger lastRow =_resultsContr.fetchedObjects.count -1;
if (lastRow < 0) {
//行数如果小于0,不能滚动
return;
}
NSIndexPath *lastPath = [NSIndexPathindexPathForRow:lastRowinSection:0];
[self.tableViewscrollToRowAtIndexPath:lastPathatScrollPosition:UITableViewScrollPositionBottomanimated:YES];
}
#pragma mark 选择图片
-(void)addBtnClick{
UIImagePickerController *imagePicker = [[UIImagePickerControlleralloc]init];
imagePicker.sourceType =UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.delegate = self;
[selfpresentViewController:imagePickeranimated:YEScompletion:nil];
}
#pragma mark 选取后图片的回调
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(@"%@",info);
// 隐藏图片选择器的窗口
[selfdismissViewControllerAnimated:YEScompletion:nil];
// 获取图片
UIImage *image = info[UIImagePickerControllerOriginalImage];
// 把图片发送到文件服务器
//http post put
/**
* put实现文件上传没post那烦锁,而且比POST快
* put的文件上传路径就是下载路径
*文件上传路径http://localhost:8080/imfileserver/Upload/Image/ + "图片名【程序员自已定义】"
*/
// 1.取文件名用户名 +时间(201412111537)年月日时分秒
NSString *user = [WCUserInfosharedWCUserInfo].user;
NSDateFormatter *dataFormatter = [[NSDateFormatteralloc]init];
dataFormatter.dateFormat = @"yyyyMMddHHmmss";
NSString *timeStr = [dataFormatter stringFromDate:[NSDate date]];
// 针对我的服务,文件名不用加后缀
NSString *fileName = [user stringByAppendingString:timeStr];
// 2.拼接上传路径
NSString *uploadUrl = [@"http://localhost:8080/imfileserver/Upload/Image/"stringByAppendingString:fileName];
// 3.使用HTTP put上传
#warning 图片上传请使用jpg格式因为我写的服务器只接接收jpg
[self.httpTooluploadData:UIImageJPEGRepresentation(image,0.75)url:[NSURLURLWithString:uploadUrl]progressBlock:nilcompletion:^(NSError *error) {
if (!error) {
NSLog(@"上传成功");
[self sendMsgWithText:uploadUrl bodyType:@"image"];
}
}];
// 图片发送成功,把图片的URL传Openfire的服务
}
-(void)dealloc{
[selfteardownXmpp];
}
#pragma mark 释放xmppStream相关的资源
-(void)teardownXmpp{
// 移除代理
[_xmppStreamremoveDelegate:self];
// 停止模块
[_reconnect deactivate];
[_vCarddeactivate];
[_avatardeactivate];
[_rosterdeactivate];
[_msgArchivingdeactivate];
// 断开连接
[_xmppStream disconnect];
// 清空资源
_reconnect = nil;
_vCard = nil;
_vCardStorage =nil;
_avatar = nil;
_roster = nil;
_rosterStorage =nil;
_msgArchiving =nil;
_msgStorage = nil;
_xmppStream = nil;
}
#pragma mark 接收到好友消息时调用的代理方法
-(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
WCLog(@"%@",message);
//如果当前程序不在前台,发出一个本地通知
if([UIApplicationsharedApplication].applicationState !=UIApplicationStateActive){
WCLog(@"在后台");
//本地通知
UILocalNotification *localNoti = [[UILocalNotificationalloc] init];
// 设置内容
localNoti.alertBody = [NSStringstringWithFormat:@"%@\n%@",message.fromStr,message.body];
// 设置通知执行时间
localNoti.fireDate = [NSDatedate];
//声音
localNoti.soundName = @"default";
//执行
[[UIApplicationsharedApplication] scheduleLocalNotification:localNoti];
//{"aps":{'alert':"zhangsan\n have dinner":'sound':'default',badge:'12'}}
}
}
//好友在线状态改变的代理方法
-(void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence{
//XMPPPresence 在线 离线
//presence.from 消息是谁发送过来
}
这篇关于iOS即时通信之XMPP框架的使用及原理简介的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!