一、用户登录流程

用户登录流程.png
  • 注意:XMPP核心文件,基于TCP的XML流的传输,XMPPFrame框架是通过代理的方式实现消息传递的

实现用户登录的步骤如下:

  • 1、实例化XMPPStream并设置代理,同时添加代理到工作队列
  • 2、使用JID连接至服务器,默认端口为5222,JID字符串中需要包含服务器的域名
  • 3、 在完成连接的代理方法中验证用户密码,连接完成后XMPPStream的isConnect属性为YES
    +4、 在验证代理方法中判断用户是否登录成功
  • 5、上线或者下线成功后,向服务器发送Presence数据,以更新用户在服务器的状态

二、注意

  • 为了简化开发,XMPP的引用程序通常会将XMPPStream放置在AppDelegate中,以便于全局访问

三、分析

1、封装登录工具类 JPLoginTool

  • 利用工具类,保存用户登录信息到沙盒中
    1. 头文件 .h
#import <Foundation/Foundation.h>

@interface JPLoginTool : NSObject

/**
* 保存登录信息到沙盒
*/
+(void)saveLoginInfoWithAccount:(NSString *)account pwd:(NSString *)pwd domain:(NSString *)domain; /**
* 获取沙盒的帐号
*
*/
+(NSString *)account; /**
* 获取沙盒的密码
*
*/
+(NSString *)password; /**
* 获取沙盒的域名
*
*/
+(NSString *)domain; /**
* 从沙盒清除所有的用户登录信息
*/
+(void)removeAllLoginInfo; /**
* 获取用户登录状态
*
*/
+(BOOL)isLogin; /**
* 设置用户登录状态
*/
+(void)setLogin:(BOOL)login;
@end
    1. .m实现文件
#import "JPLoginTool.h"

#define kAccountKey @"account"
#define kPasswordKey @"passsword"
#define kDomainKey @"domain"
#define kIsLoginKey @"isLogin" @implementation JPLoginTool +(void)saveLoginInfoWithAccount:(NSString *)account pwd:(NSString *)pwd domain:(NSString *)domain{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:account forKey:kAccountKey];
[defaults setObject:pwd forKey:kPasswordKey];
[defaults setObject:domain forKey:kDomainKey]; //同步沙盒
[defaults synchronize];
} /**
* 获取沙盒的帐号
*
*/
+(NSString *)account{ return [[NSUserDefaults standardUserDefaults] objectForKey:kAccountKey];
} /**
* 获取沙盒的密码
*
*/
+(NSString *)password{
return [[NSUserDefaults standardUserDefaults] objectForKey:kPasswordKey];
} /**
* 获取沙盒的域名
*
*/
+(NSString *)domain{
return [[NSUserDefaults standardUserDefaults] objectForKey:kDomainKey];
} /**
* 从沙盒清除所有的用户登录信息
*
*/
+(void)removeAllLoginInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:kAccountKey];
[defaults removeObjectForKey:kPasswordKey];
[defaults removeObjectForKey:kDomainKey];
[defaults synchronize];
} /**
* 获取用户登录状态
*
*/
+(BOOL)isLogin{
return [[NSUserDefaults standardUserDefaults] boolForKey:kIsLoginKey];
} /**
* 设置用户登录状态
*/
+(void)setLogin:(BOOL)login{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:login forKey:kIsLoginKey];
[defaults synchronize];
}
@end

2、AppDelegate.h 文件中

  • 1、首先,我们布局登录界面,提供登录按钮,当我们点击登录按钮时就进行登录操作,因为XMPPFrame的相关操作都建议写在AppDelegate文件中,所以我们需要在AppDelegate.h文件中提供接口给用户,而至于那些连接到服务器以及授权等操作都写在AppDelegate.m中不用暴露给用户。

    #pragma mark 用户登录
    -(void)xmppUserLogin;
  • 2、但是,当我们点击登录,在控制器的登录方法中,拿到应用程序的代理,然后调用应用代理的xmppUserLogin方法,此时不仅要执行登录操作,而且应用代理必须要告诉我们是否登录成功,因为登录操作是封装到AppDelegate中的 - > 这就要实现两者之间的通信了。
    • 有多种通信方式,通知/代理/block/action等,这里我选择代理,所以重新设置应用代理提供的登录接口为:
// 在应用代理头文件中

// 1. 定义block

typedef enum {
XMPPResultTypeLogining,//正在登录中
XMPPResultTypeLoginSuccess,//登录成功
XMPPResultTypeLoginFailure,//登录失败
XMPPResultTypeNetError,//网络不给力
XMPPResultTypeUnknowDomain,//域名不存在
XMPPResultTypeConnectionRefused//服务器拒绝连接
} XMPPResultType; typedef void (^XMPPResultBlock)(XMPPResultType resultType);//xmpp请求结果的block // 2.提供为外界登录接口
#pragma mark 用户登录
-(void)xmppUserLogin:(XMPPResultBlock)resultBlock;
  • 3、用户登录成功后,可以点击控制器视图中的注销按钮进行注销操作,所以我们在AppDelegate中提供了一个注销方法接口给外界
#pragma 用户注销
-(void)xmppUserLogout;
  • 4、当我们的用户状态改变的时候,要告诉外界,比如,用户在线时,需要一些处理,所以我们不仅需要将用户的登录信息写入到沙盒中,而且用户的状态也要写入到沙盒中,第二个问题:当我们的用户状态改变的时候,我们要告诉控制器,让控制器进行一些处理操作:又牵扯到通信了,这里:采用通知告知吧
//登录状态改变
#define kLoginStateChangeNotification @"LoginStateChangeNotification"
  • 5、AppDelegate.h头文件
#import <UIKit/UIKit.h>

#define kIsLoginKey @"isLogin"

//登录状态改变
#define kLoginStateChangeNotification @"LoginStateChangeNotification" #define xmppDelegate ((JPAppDelegate *)[UIApplication sharedApplication].delegate) typedef enum {
XMPPResultTypeLogining,//正在登录中
XMPPResultTypeLoginSuccess,//登录成功
XMPPResultTypeLoginFailure,//登录失败
XMPPResultTypeNetError,//网络不给力
XMPPResultTypeUnknowDomain,//域名不存在
XMPPResultTypeConnectionRefused//服务器拒绝连接
}XMPPResultType;
typedef void (^XMPPResultBlock)(XMPPResultType resultType);//xmpp请求结果的block @interface JPAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; #pragma mark 用户登录
-(void)xmppUserLogin:(XMPPResultBlock)resultBlock; #pragma 用户注销
-(void)xmppUserLogout; @end

三、AppDelegate.m实现文件

  • 0.在登录界面点击登录按钮,登录成功后,进入主界面
  • 1.用户登录成功后,退出到后台时,断开连接,显示在前台时自动连接(添加一个isLogin用户偏好设置)
  • 2.用户成功登录后,如果是重新启动程序,直接跳到主界面,否则跳到登录页面
  • 3.用户成功登录后,如果是重新启动程序,下次启动时自动登录
  • 4.用户登录失败时,清除偏好设置
  • 5.用户登录失败时要提示
  • 6.用户注销
  • 7.进入登录页面时,自动显示上一次登录数据

  • 注意:

    • 官方建议,把用户授权写在appdelegate
#import "JPAppDelegate.h"
#import "JPLoginTool.h" #define kMainStoryboardName @"Main"
#define kLoginStoryboardName @"Login" @interface JPAppDelegate()<XMPPStreamDelegate>{ XMPPResultBlock _resultBlock;
} //初始化xmppStream
-(void)setupXmppStream; //连接主机
-(void)connectToHost; //从主机断开连接
-(void)disconnectFromHost; //授权(也就发送帐号和密码)
-(void)userAuth; //用户上线(通知其他好友,你已经在线)
-(void)goOnline; //用户用户离线
-(void)goOffline; @end @implementation JPAppDelegate //官方建议,把用户授权写在appdelegate
/**
* 0.初始化xmppStream
* 1.连接主机
* 2.从主机断开连接
* 3.授权(也就发送帐号和密码)
* 4.用户上线(通知其他好友,你已经在线)
* 5.用户用户离线
*/ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ //NSLog(@"%s %d",__func__,__LINE__); //设置日志输出方式(输出到控制器)
[DDLog addLogger:[DDTTYLogger sharedInstance]]; //设置颜色
[[DDTTYLogger sharedInstance] setColorsEnabled:YES]; //设置自己的日志颜色
[[DDTTYLogger sharedInstance] setForegroundColor:[UIColor blueColor] backgroundColor:nil forFlag:LOG_LEVEL_INFO]; // 初始化xmppStream
[self setupXmppStream]; return YES;
}
#pragma 失去焦点
- (void)applicationWillResignActive:(UIApplication *)application
{
//从主机断开连接
//[self disconnectFromHost]; //获取登录状态
if([JPLoginTool isLogin]){
//NSLog(@"登录过");
//如果是登录 断开连接
[self disconnectFromHost];
};
} #pragma mark 获取焦点
- (void)applicationDidBecomeActive:(UIApplication *)application
{
//[self connectToHost];
//获取登录状态 如果登录过,直接跳到主页面 然后自动登录
if([JPLoginTool isLogin]){
NSLog(@"登录过");
//直接跳到主页
[self showStoryboardWithName:kMainStoryboardName]; //自动登录
[self connectToHost];
};
} #pragma mark - xmppSteam代理
#pragma mark 连接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
JPLogInfo(@"与主机连接成功");
//JPLogInfo(@"登录");
//发送登录密码验证
[self userAuth];
} #pragma mark 连接失败
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{ //如果error为空,代理正常断开
JPLogInfo(@"连接失败 %@",error); //有error 并且block有值 通知登录控制器 if (error && _resultBlock) {
//域名或者主机不存
if (error.code == 8) {
_resultBlock(XMPPResultTypeUnknowDomain);
}else if(error.code == 61){
_resultBlock(XMPPResultTypeConnectionRefused);
} else{
_resultBlock(XMPPResultTypeNetError);
}
} // 清除用户偏好设置
if (error) {
[JPLoginTool removeAllLoginInfo];
}
} #pragma mark 用户授权成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
// 子线程
NSLog(@"%@",[NSThread currentThread]);
JPLogInfo(@"用户授权成功");
// 把"登录状态"通知"聊天历史控制器"
// 登录成功
[self postLoginNotification:XMPPResultTypeLoginSuccess]; // 1.通知用户上线
[self goOnline]; // 2.如果用户登录成功 沙盒里保存一个登录状态
[JPLoginTool setLogin:YES]; //在主线程更新UI
[self showStoryboardWithName:kMainStoryboardName];
} #pragma mark 用户授权失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
JPLogInfo(@"用户授权失败%@",error);
//把"登录状态"通知"聊天历史控制器"
//登录失败
[self postLoginNotification:XMPPResultTypeLoginFailure]; //清除用户偏好设置
[JPLoginTool removeAllLoginInfo];
//通知登录控制器
if (_resultBlock) {
_resultBlock(XMPPResultTypeLoginFailure);
}
// dispatch_async(dispatch_get_main_queue(), ^{
//
// });
} #pragma mark -私有方法 #pragma mark 初始化xmppStrem对象
-(void)setupXmppStream{ NSAssert(_xmppStream == nil, @"xmppStream对象初始化多次"); //1.创建xmppStrem对象
_xmppStream = [[XMPPStream alloc] init];
//2.添加代表
[_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
} -(void)connectToHost{
JPLogInfo(@"开始连接到主机"); //把"登录状态"通知"聊天历史控制器"
//登录中
[self postLoginNotification:XMPPResultTypeLogining]; //从沙盒里获取数据
NSString *account = [JPLoginTool account];
NSString *domain = [JPLoginTool domain]; //1.设置账号
_xmppStream.myJID = [XMPPJID jidWithUser:account domain:domain resource:nil]; //2.设置主机
_xmppStream.hostName = domain; //3.设置主机端口
//默认端口就5222
_xmppStream.hostPort = 5222;
//连接到主机 NSError *err = nil;
[_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err];
if (err) {
JPLogInfo(@"%@ ",err);
}
} #pragma mark 从主机断连接
-(void)disconnectFromHost{
// 1.通知用户下线
[self goOffline]; // 2.断开连接
[_xmppStream disconnect];
} #pragma mark 用户授权
-(void)userAuth{
JPLogInfo(@"用户开始授权"); NSError *error = nil;
//发送密码到服务器
[_xmppStream authenticateWithPassword:[JPLoginTool password] error:&error]; if (error) {
JPLogInfo(@"%@",error);
}
} #pragma mark 用户上线
-(void)goOnline{
JPLogInfo(@"通知用户在线");
XMPPPresence *presence = [XMPPPresence presence];
[_xmppStream sendElement:presence];
} #pragma mark 用户下线
-(void)goOffline{
JPLogInfo(@"通知用户下线");
XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppStream sendElement:presence];
} #pragma mark 显示storyboard的第一个控制器
// 比如用户登录成功后,设置主窗口的根控制器为tabVC,然后,选中的tabVC的第三个子控制器,
// 然后退出到后台,当用户再次进入到前台时,并不需要重新设置窗口的根控制器此时。
// 那什么时候需要设置呢-> 当主窗口的根控制器不是tabVC才需要重新设置窗口的根控制器
-(void)showStoryboardWithName:(NSString *)name{
dispatch_async(dispatch_get_main_queue(), ^{
// 3.显示主界面
// 3.1获取sotryboard对象
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:name bundle:nil]; //3.2获取windows的旧的根控制器
UIViewController *oldVc = self.window.rootViewController;
JPLogWarn(@"旧的控制器 %@",oldVc); //3.3获取要切换storyboard里面的第一个控制器
UIViewController *newVc = [storyboard instantiateInitialViewController]; JPLogWarn(@"要切换的控制器 %@",newVc); //如果旧的控制器类型和新的控制器类型不一样,才须要切换UIWindow的根控制器
if (![oldVc isKindOfClass:[newVc class]]) {
//38
self.window.rootViewController = newVc;
}
});
} #pragma mark -公共方法
#pragma mark 用户登录
-(void)xmppUserLogin:(XMPPResultBlock)resultBlock{
//block负值
_resultBlock = resultBlock; JPLogInfo(@"用户登录被调用");
//如果当前socket存在连接,应该断开
// if (_xmppStream.isConnected) {
// [_xmppStream disconnect];
// } [_xmppStream disconnect];
[self connectToHost];
} #pragma mark 用户注销
-(void)xmppUserLogout{
//1.设置登录状态为NO
[JPLoginTool setLogin:NO]; //2.断开连接
[self disconnectFromHost]; //3.返回到登录页面
[self showStoryboardWithName:@"Login"];
} #pragma mark 登录状态通知
-(void)postLoginNotification:(XMPPResultType)resultType{
//把"登录状态"通知"聊天历史控制器"
//登录成功
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *userInfo = @{@"LoginState": @(resultType)}; [[NSNotificationCenter defaultCenter] postNotificationName:kLoginStateChangeNotification object:nil userInfo:userInfo];
});
}

四、控制器中实现登录

- (IBAction)login {

    //1.把你登录信息保存到沙盒里
NSString *account = self.accountField.text;
NSString *password = self.passwordFiled.text;
NSString *domain = self.domainField.text; [JPLoginTool saveLoginInfoWithAccount:account pwd:password domain:domain]; //隐藏keyboard
[self.view endEditing:YES]; //提醒正在登录
UIView *showView = self.view;
[MBProgressHUD showMessage:@"正在登录......" toView:showView]; xmppDelegate.userRegister = NO;//代表登录 //2.调用appdelegate里的xmmpUserLogin方法
//JPAppDelegate *delegate = [UIApplication sharedApplication].delegate;
[xmppDelegate xmppUserLogin:^(XMPPResultType resultType) {
//隐藏正在登录
//因为这个block是被appdelegate里面xmppStream的代理调用,而xmppStream代理被调用是在子线线程中的,所在更新UI放在主线程
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:showView];
switch (resultType) {
case XMPPResultTypeLoginFailure:
[MBProgressHUD showError:@"用户名或者密码错误"];
break;
case XMPPResultTypeNetError:
[MBProgressHUD showError:@"网络不给力"];
break;
case XMPPResultTypeConnectionRefused:
[MBProgressHUD showError:@"服务器拒绝连接,可能服务没有开启"];
break;
case XMPPResultTypeUnknowDomain:
JPLogInfo(@"域名不存在或者错误");
[MBProgressHUD showError:@"域名不存在或者错误"];
break;
default:
break;
}
}); }];
}

原文链接:http://www.jianshu.com/p/a16d3d70dd86

参考链接:http://blog.csdn.net/cerastes/article/details/33713967

            http://blog.csdn.net/liuhongwei123888/article/details/6840262

            http://www.jianshu.com/p/a22406c40c8b

 源码地址:https://github.com/robbiehanson/XMPPFramework,目前需要使用 Git (sourcTree) 才能download到源码。

XMPPManager 解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  3. Html Agility Pack 解析Html

    Hello 好久不见 哈哈,今天给大家分享一个解析Html的类库 Html Agility Pack.这个适用于想获取某网页里面的部分内容.今天就拿我的Csdn的博客列表来举例. 打开页面  用Fir ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  8. 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye

    一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...

  9. SQL Server 数据加密功能解析

    SQL Server 数据加密功能解析 转载自: 腾云阁 https://www.qcloud.com/community/article/194 数据加密是数据库被破解.物理介质被盗.备份被窃取的最 ...

随机推荐

  1. Mybatis框架的输出映射类型

    Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心. resultType(输出类型) 1.输出简单类型 (1)我们在UserM ...

  2. mybatis框架中的输入映射

    mybatis.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心. 输入类型: 1.传递简单类型 可以参考我之前的对于数据库增删改查的博文. ...

  3. entity_class实体类

    对应数据库中表,并继承基础模型类~

  4. 3 python之基础概要

    一: 与用户交互 1 什么事与用户交互 程序等待用户输入一些数据,程序执行完毕之后为用户反馈信息 2 为什么程序要与用户交互 为了让计算机像人一样和用户沟通 3 如何用: 在python3中:inpu ...

  5. 看图说话:关于BI那点事儿

    [编者按]BI=DW+数据挖掘+业务分析+社会学?BI三部曲:管数据.看数据.源数据.BI有三种放法:技术部.业务部和独立部门.BI的工作=20%数据平台+30%数据支持+50%数据应用.  

  6. POJ 2396 Budget (有源汇有上下界最大流)

    题意:给定一个矩阵的每行的和和每列的和,以及每个格子的限制,让你求出原矩阵. 析:把行看成X,列看成Y,其实就是二分图,然后每个X到每个Y边一条边,然后加一个超级源点和汇点分别向X和Y连边,这样就形成 ...

  7. Python入门基础学习 二

    Python入门基础学习 二 猜数字小游戏进阶版 修改建议: 猜错的时候程序可以给出提示,告诉用户猜测的数字偏大还是偏小: 没运行一次程序只能猜测一次,应该提供多次机会给用户猜测: 每次运行程序,答案 ...

  8. git@oschina使用入门(图形界面版)

    首先,如果你想使用git@oschina ,你的电脑上必须先有git工具:你可以从这里获取谷歌提供的git.exe http://git-scm.com/当然,如果你能熟练通过命令行操作git,那么这 ...

  9. sql查询优化--数字转换字符串字段

    SELECT top 1 pt.* FROM t1where id='20180731223014' SELECT top 1 pt.* FROM t1where id='0180731223014 ...

  10. UVA 11426 GCD - Extreme (II)(欧拉函数打表 + 规律)

    Given the value of N, you will have to find the value of G. The definition of G is given below:Here ...