xmpp中的用户连接模块包括用户的上线与下线信息展现,用户登录,用户的注册; 好友模块包括好友的添加,好友的删除,好友列表的展示。

在xmpp中 负责数据传输的类是xmppStream,开发的过程中,针对不同的传输内容,会调用不同的代理方法,在使用XMPPFramework开发时,只需要在不同的代理方法中,填写相同的代码即可。

往期回顾:

xmpp整理笔记:xmppFramework框架的导入和介绍  http://www.cnblogs.com/dsxniubility/p/4307057.html

xmpp整理笔记:环境的快速配置(附安装包)  http://www.cnblogs.com/dsxniubility/p/4304570.html

如果你不是在董铂然博客园看到本文,请点击查看原文

一。大概的连接过程如下

1.运行后需要和服务器建立一个长连接,系统会反馈链接是否成功

2.成功时需要告诉服务器的用户的密码,服务器判断是否给予授权

3.成功授权后,告诉服务器上线了。

4.将要离开时告诉服务器,我需要断开链接了。

5.服务器反馈你可以断开了,然后你再告诉服务器你下线了

二。首先,需要知道  XMPPStreamDelegate 和  XMPPRosterDelegate 的一些代理方法

如果你不是在董铂然博客园看到本文 请点击查看原文

xmpp流代理方法:

连接成功时调用

  - (void)xmppStreamDidConnect:(XMPPStream *)sender

断开连接时调用

  - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error

授权成功时调用

  - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender

授权失败时调用

  -(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error

注册成功时调用

  - (void)xmppStreamDidRegister:(XMPPStream *)sender

注册失败时调用

  - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error

xmppRoster花名册代理方法 

接收到好友请求时调用

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

三。用户的登录:

用户需要在连接成功后的代理方法中 将自己的密码发送给服务器,自己的密码应该是在点击登录的时候就和其他信息一起存入偏好设置了,在现在需要的时候可以轻而易举的取出来。在发送验证请求的时候会用到这个方法authenticateWithPassword: 后面的error在实际开发中建议必须处理,我在这就偷懒了如下所示

/** 连接成功时调用 */
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
NSLog(@"连接成功"); NSString *password = [[NSUserDefaults standardUserDefaults] valueForKey:SXLoginPasswordKey]; // 将用户密码发送给服务器,进行用户登录
[self.xmppStream authenticateWithPassword:password error:NULL];
}

然后等待结果,在授权成功后来到授权成功代理方法在这应该先告诉服务器用户上线,然后给发出成功通知,自己的AppDelegate在远处接收,一旦接收到通知马上更换应用程序的根控制器到进入后的界面,这里要注意这些代理方法都是在异步的,所以这里要用到线程间通讯,在主线程发送通知

    // 通知服务器用户上线
[self goOnline];
// 在主线程利用通知发送广播
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SXLoginResultNotification object:@(YES)];
});

如果授权失败的话,应该断开与服务器的链接,并且把开始存储的用户偏好清空(因为这些是错误的没用),然后再到主线程更新UI弹出一个框显示密码错误,并且发出失败通知,让APPDelegate切换根控制器到登录界面 (董铂然原创)

// 断开与服务器的连接
[self disconnect];
// 清理用户偏好
[self clearUserDefaults];
// 在主线程更新UI
if (self.failed) {
dispatch_async(dispatch_get_main_queue(), ^ {self.failed(@"用户名或者密码错误!");});
}
// 在主线程利用通知发送广播
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SXLoginResultNotification object:@(NO)];
});

四。用户的上线和下线:

关于用户的上线和下线,需要用到一个类XMPPPresence 类。这个类是XMPPElement的子类,主要用来管理某些信息的展现。首先要实例化一个对象,这其中会用到一个presenceWithType 方法,有两个选择@"unavailable"代表下线,@"available"代表上线,一般情况上线的时候后面就可以直接省略。实例化之后用xmpp流发出去。如下所示

#pragma mark - ******************** 用户的上线和下线
- (void)goOnline {
XMPPPresence *p = [XMPPPresence presence]; [self.xmppStream sendElement:p];
} - (void)goOffline {
XMPPPresence *p = [XMPPPresence presenceWithType:@"unavailable"]; [self.xmppStream sendElement:p];
}

对用户是否在线状态的判断

// 取出用户
XMPPUserCoreDataStorageObject *user = [self.fetchedResultsController objectAtIndexPath:indexPath];

用户的 user.section 就是用户的状态

// section    // 0 在线    // 1 离开   // 2 离线

五。用户注册:

自己在UI里搭建好注册页面,里面需要用户填写好用户信息。在点击注册按钮时,把单例类里自己设定的一个布尔值isRegisterUser 设置为YES。 然后重新发送连接请求。最终还是会来到,连接成功时的代理方法,刚才在这里发送用户密码登录的,现在可以加一层判断,如果isRegisterUser的值为YES 就不是发送用户密码登录了,而是发送用户密码注册,这里将会用到一个方法registerWithPassword:

if (self.isRegisterUser) {
// 将用户密码发送给服务器,进行用户注册
[self.xmppStream registerWithPassword:password error:NULL];
// 将注册标记复位
self.isRegisterUser = NO;
}

然后有两个代理方法,注册成功和注册失败,分别写上合适的操作。

六。添加好友:

搭建一个加好友的UI只需要一个文本框和一个按钮。

在文本框的回车按钮点击代理方法中,做文本框是否为空得判断,不为空就添加好友,(添加好友方法可以抽出来写使得结构更加清晰)

添加好友方法如下:有两个注意点一个是判断用户是否写了域名,如果只是单单写了个账号,也可以自动帮他拼接个域名然后注册。还有个就是判断是否已经是自己的好友,如果是就不做任何操作。如果不是好友 那就马上添加。最后让导航控制器返回到登陆界面

// 添加好友
- (void)addFriendWithName:(NSString *)name { // 你写了域名那更好,你没写系统就自动帮你补上
NSRange range = [name rangeOfString:@"@"];
// 如果没找到 NSNotFound,不要写0
if (range.location == NSNotFound) {
name = [name stringByAppendingFormat:@"@%@", [SXXMPPTools sharedXMPPTools].xmppStream.myJID.domain];
} // 如果已经是好友就不需要再次添加
XMPPJID *jid = [XMPPJID jidWithString:name]; BOOL contains = [[SXXMPPTools sharedXMPPTools].xmppRosterCoreDataStorage userExistsWithJID:jid xmppStream:[SXXMPPTools sharedXMPPTools].xmppStream]; if (contains) {
[[[UIAlertView alloc] initWithTitle:@"提示" message:@"已经是好友,无需添加" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];
return;
} [[SXXMPPTools sharedXMPPTools].xmppRoster subscribePresenceToUser:jid]; [self.navigationController popViewControllerAnimated:YES];
}

这里会用到一个通过JID加好友的方法subscribePresenceToUser: 但是这个方法是通过Roster 调用的所以要在单例类里导入头文件 声明属性,遵守协议,实现代理方法(董铂然原创)

在单例类里所有特殊类的操作都要写在xmppStream的懒加载里

        // 实例化
_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)的人请求加你为好友。然后设置弹窗,确定和拒绝,点击确定按钮后

// 接受好友请求
[self.xmppRoster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];

这个弹窗建议使用iOS8的新功能  UIAlertController。 这样可以不用写alertDelegate 也能设置确定按钮点击事件 。用 alert addAction: 添加按钮,把点击事件写在block里,最后再取到当前窗口的根控制器弹出presentViewController,相当于以前的show 。iOS8苹果的思想渐渐是想把所有弹出控制器的各种方法都慢慢统一到present。

补充:这个功能就是QQ上所谓的加好友不需要验证,是布尔值可以控制开关。

        // 取消接收自动订阅功能,需要确认才能够添加好友!
_xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = NO;

七。好友列表的展示。

这里需要用到查询结果调度器

- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
// 指定查询的实体
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"XMPPUserCoreDataStorageObject"]; // 在线状态排序
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; // 实例化结果控制器
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request managedObjectContext:ctx sectionNameKeyPath:nil cacheName:nil]; // 设置他的代理
_fetchedResultsController.delegate = self; return _fetchedResultsController;
}

写完了结果调度器之后要切记在viewdidload页面首次加载中加上一句,否则不干活

// 查询数据
[self.fetchedResultsController performFetch:NULL];

结果调度器有一个代理方法,一旦上下文改变触发,也就是刚加了好友,或删除好友时会触发

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSLog(@"上下文改变");
[self.tableView reloadData];
}

整个tableview的数据源方法如下

- (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]; // 显示此好友是否相互关注
NSString *str = [user.jidStr stringByAppendingFormat:@" | %@",user.subscription]; cell.textLabel.text = str ;
// 这里有个自定义方法传入section 通过switch判断返回汉字。section关系到是否在线
cell.detailTextLabel.text = [self userStatusWithSection:user.section]; return cell;
}

其中subscription是用户的好友互加情况

// 如果是none表示对方还没有确认   // to 我关注对方  // from 对方关注我  // both 互粉

再提一下 user.section 就是用户的状态

// section    // 0 在线    // 1 离开   // 2 离线

当有好友上线,上下文改变时,结果调度器会重新排序,然后在线的好友会显示在上面。

八。删除好友

好友的列表显示界面可以给tableView添加滑动删除。(开启编辑模式)

#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;
// 接下来是设置弹窗

在弹窗的点击事件里面删除好友用到的方法是

[[SXXMPPTools sharedXMPPTools].xmppRoster removeUser:jid];

如果你不是在董铂然博客园看到本文,请点击查看原文

正在整理关于信息发送模块的各种细节,有兴趣的可以关注

xmpp整理笔记:用户网络连接及好友的管理的更多相关文章

  1. xmpp整理笔记:发送图片信息和声音信息

    图片和音频文件发送的基本思路就是: 先将图片转化成二进制文件,然后将二进制文件进行base64编码,编码后成字符串.在即将发送的message内添加一个子节点,节点的stringValue(节点的值) ...

  2. xmpp整理笔记:聊天信息的发送与显示

    任何一个信息的发送都需要关注两个部分,信息的发出,和信息在界面中的显示 往期回顾: xmpp整理笔记:环境的快速配置(附安装包)  http://www.cnblogs.com/dsxniubilit ...

  3. xmpp整理笔记:xmppFramework框架的导入和介绍

    一个将要开发xmpp的项目,建议在项目刚创建就导入框架,这样可以避免一些自己操作失误造成不必要的损失. xmpp中最常用的框架就是 xmppFrameWork 往期回顾: xmpp整理笔记:环境的快速 ...

  4. xmpp整理笔记:环境的快速配置(附安装包)

    现在虽然环信的xmpp框架很火,但是也有一些弊端.环信的框架部分代码不开源,而且收费模式不科学,用户量一直低于免费线则好,一旦超过,收费极高. xmpp感觉还是从xmppFramework框架学起比较 ...

  5. lesson - 1 笔记 网络连接 /putty 密钥登陆

    ---笔记 一.网络连接配置 1. 查看电脑ip 地址: ifconfig -a  2. 自动获取ip 地址: dhclient   默认BOOTPROTO=dhcp  和真机同一网段 3. 手动配置 ...

  6. Hi3516开发笔记(二):Hi3516虚拟机基础环境搭建之串口调试、网络连接以及sftp文件传输

    前言   搭建Hi3516的基础虚拟机,为交叉编译环境搭建前期工作.后续会编译一个基本的C语言程序Demo,在HI3516上跑.   虚拟机   开发本对虚拟机做了一些基本要求,如下图:    其实重 ...

  7. Silverlight项目笔记6:Linq求差集、交集&检查网络连接状态&重载构造函数复用窗口

    1.使用Linq求差集.交集 使用场景: 需要从数据中心获得用户数据,并以此为标准,同步系统的用户信息,对系统中多余的用户进行删除操作,缺失的用户进行添加操作,对信息更新了的用户进行编辑操作更新. 所 ...

  8. Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】

    一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...

  9. Kafka技术内幕 读书笔记之(二) 生产者——服务端网络连接

    KafkaServer是Kafka服务端的主类, KafkaServer中和网络层有关的服务组件包括 SocketServer.KafkaApis 和 KafkaRequestHandlerPool后 ...

随机推荐

  1. Windows Azure Virtual Machine (24) Azure VM支持多网卡功能

    <Windows Azure Platform 系列文章目录> Update 2016-03-30 一个VM的多张网卡可以在一个VNet的同一个Subnet子网下,笔者亲自测试过了. 如果 ...

  2. UWP开发入门(二十三)——WebView

    本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的 ...

  3. When using SqlDependency without providing an options value, SqlDependency.Start() must be called prior to execution of a command added to the SqlDependency instance.

    在调试SignalR程序时,提示一个异常:When using SqlDependency without providing an options value, SqlDependency.Star ...

  4. LINQ多个操作嵌套实现

    获取集合,需要使用多个条件Where,排序OrderBy,查询Select一起. 先来分步实现: source code: string[] stringArray = { "hgdgh&q ...

  5. ASP.NET Core 开发-缓存(Caching)

    ASP.NET Core 缓存Caching,.NET Core 中为我们提供了Caching 的组件. 目前Caching 组件提供了三种存储方式. Memory Redis SqlServer 学 ...

  6. asp.net session

    简介编辑 session对象用于存储特定的用户会话所需的信息 . Session对象的引入是为了弥补HTTP协议的不足,HTTP协议是一种无状态的协议. Session中文是"会话" ...

  7. Bootstrap学习笔记系列5------Bootstrap图片显示

    通过添加一下的class来实现bootstrap对图片的支持 img-round 通过border-radius:6px 来获得图片圆角 img-circle 通过border-radius:50%来 ...

  8. wpf 自定义圆形按钮

    wpf 自定义圆形按钮 效果图 默认样式 获取焦点样式 点击样式 下面是实现代码: 一个是自定义控件类,一个是控件类皮肤 using System; using System.Collections. ...

  9. script引入js文件问题

  10. 使用Toast的setText()实现倒计时

    点击按钮发送短信以后,倒计时3秒钟后退出Activity.本来想用弹出多个Toast的方法实现,后来发现效果不好,因为Toast.LENGTH_LONG显示3秒钟,Toast.LENGTH_SHORT ...