【原】iOS学习42即时通信之XMPP(1)

1. 即时通信

 1> 概述

  即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷,服务提供商也提供了越来越丰富的通讯服务功能。

  即时通讯有多种实现方式,例如:XMPP、环信、融云等。

 2> XMPP(可扩展消息处理现场协议)

  XMPP(XML Messages Presence Protocol(可扩展消息处理现场协议))是基于可扩展标记语言(XML)的协议,可用于即时通信(IM)。

 3> XMPP的优点

  • 开放

  XMPP协议是由JSF开源社区组织开发的,不属于任何的机构和个人,从根本上保证了其开放性。

  • 标准

  互联网工程工作组将XMPP于2002年正式列为认可的实时通讯及Presence技术。

  • 分布式的网络架构

  XMPP 协议虽然是基于Client/Server 架构,但是XMPP协议本身并没有这样的限制。网络的架构和电子邮件十分相似,但没有结合任何特定的网络架构,适用范围非常广泛。

  • 具有良好的扩展性

  基于XML 建立起来的应用具有良好的语义完整性和扩展性。

  • 具有很好的弹性

  XMPP 除了可用在即时通信的应用程序,还能用在网络管理、内容供稿、协同工具、档案共享、游戏、远端系统监控等。

  • 安全性

  XMPP在Client-to-Server通信和Server-to-Server通信中都使用TLS (Transport Layer Security)协议作为通信通道的加密方法,保证通信的安全。

  XMPP环境的搭建的步骤详见XMPP环境的搭建

2. 工程中添加XMPP

  将XMPP协议第三方文件拖入工程,下载地址:https://github.com/AlonerOwl/XMPPFramework

3. 登录、注册

 1> 界面搭建

  界面采用Storyboard,效果图如下:

 2> 初始化XMPP通道

        //初始化通道
        self.xmppStream = [[XMPPStream alloc] init];
        //openfire服务器IP地址
        self.xmppStream.hostName = kHostName;
        //openfire服务器端口 默认5222
        self.xmppStream.hostPort = kHostPort;
        //添加代理
        [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()
         ];

 3> 连接服务器代码所需方法

 4> 登录代码所需方法

 5> 注册代码所需方法

 

 6> 全部代码(方法的使用详见代码注释)

  • 封装的XMPP控制器代码:

   XMPPManager.h

#import <Foundation/Foundation.h>

#import "XMPPFramework.h"

@interface XMPPManager : NSObject

/// 声明XMPP通道属性
@property (nonatomic, strong) XMPPStream *xmppStream;

+ (XMPPManager *)shareXMPPManager;

// 登录方法
- (void)loginWithUsername:(NSString *)username
                 password:(NSString *)password;
// 注册方法
- (void)registerWithUsername:(NSString *)username
                    password:(NSString *)password;

@end

  XMPPManager.m

#import "XMPPManager.h"

static XMPPManager *xmppManager = nil;

// 枚举记录状态
typedef NS_ENUM(NSUInteger, ConnectToServerPurpose) {
    ConnectToServerPurposeLogin,
    ConnectToServerPurposeRegister
};

// 遵循协议
@interface XMPPManager () <XMPPStreamDelegate>

/// 用户名
@property (nonatomic, copy) NSString *username;

/// 密码
@property (nonatomic, copy) NSString *password;

/// 记录当前的状态,判断是登录还是注册
@property (nonatomic) ConnectToServerPurpose connectToServer;

@end

@implementation XMPPManager

+ (XMPPManager *)shareXMPPManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        xmppManager = [XMPPManager new];
    });
    return xmppManager;
}

#pragma mark - 1.创建通道
- (instancetype)init
{
    self = [super init];
    if (self) {
        //初始化对象
        self.xmppStream = [XMPPStream new];
        
        // 设置服务器
        [self settingUpServer];
    }
    return self;
}

// 设置服务器
- (void)settingUpServer
{
    // 设置opfire服务器
    self.xmppStream.hostName = kHostName;
    // 设置服务器端口号
    self.xmppStream.hostPort = kHostPort;
    // 添加代理
    [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

#pragma mark - 登录的方法
- (void)loginWithUsername:(NSString *)username
                 password:(NSString *)password
{
    self.username = username;
    self.password = password;
    
    // 记录登录状态
    self.connectToServer = ConnectToServerPurposeLogin;
    
    // 连接服务器
    [self linkServer];
}

#pragma mark - 注册的方法
- (void)registerWithUsername:(NSString *)username
                    password:(NSString *)password
{
    self.username = username;
    self.password = password;

    // 记录注册状态
    self.connectToServer = ConnectToServerPurposeRegister;
    
    // 连接服务器
    [self linkServer];
}

#pragma mark - 连接服务器
- (void)linkServer
{
    // 要连接服务器,要有用户身份认证
    // 身份证
    // 参数1:用户名
    // 参数2:域名
    // 参数3:资源
    XMPPJID *jid = [XMPPJID jidWithUser:self.username domain:kDomin resource:kResource];
    
    self.xmppStream.myJID = jid;
    
    // 如果当前聊天工具处于连接状态或已经连接,此时,你需要去切断连接【此逻辑不唯一】
    if ([self.xmppStream isConnected] || [self.xmppStream isConnecting]) {
        // 断开连接
        [self disConnectToServer];
    }
    
    // 设置连接超时
    NSError *error = nil;
    [self.xmppStream connectWithTimeout:30 error:&error];
    if (error) {
        NSLog(@"连接超时");
    }
    
}

#pragma mark - 断开连接(注销)
- (void)disConnectToServer
{
    // 当前用户不可用(下线了)
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
    // 告诉通道用户不可用(下线了)
    [self.xmppStream sendElement:presence];
    // 使用通道失去连接
    [self.xmppStream disconnect];
}

#pragma mark - XMPPStreamDelegate协议方法

#pragma mark 连接超时
- (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender
{
    NSLog(@"连接超时");
}

#pragma mark 连接成功
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    NSLog(@"连接成功");
    
    // 认证密码
//    [self.xmppStream authenticateWithPassword:self.password error:nil];
    
    // 区分登录和注册进行密码验证
    switch (self.connectToServer) {
        case ConnectToServerPurposeLogin:
            // 登录认证
            [self.xmppStream authenticateWithPassword:self.password error:nil];
            break;
            
        case ConnectToServerPurposeRegister:
            // 注册的认证
            [self.xmppStream registerWithPassword:self.password error:nil];
            break;
            
        default:
            break;
    }
}

#pragma mark 断开连接
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    // 1. 自己动手断开
    // 2. 被动断开
    if (error) {
        NSLog(@"断开连接,error == %@", error);
    }
}

#pragma mark 认证失败
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
{
    NSLog(@"认证失败:error = %@", error);
}

#pragma mark 认证成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    // 当前用户可用(上线了)
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
    // 告诉通道用户可用(上线了)
    [self.xmppStream sendElement:presence];
    
    NSLog(@"认证成功");
}

@end
  • 登录界面

  LoginViewController.m

#import "LoginViewController.h"

#import "XMPPManager.h"

@interface LoginViewController ()

/// 用户输入框
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;

/// 密码输入框
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;

@end

@implementation LoginViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)loginAction:(UIButton *)sender
{
    [[XMPPManager shareXMPPManager] loginWithUsername:self.usernameTextField.text password:self.passwordTextField.text];
}

@end
  • 注册界面

   RegisterViewController.m

#import "RegisterViewController.h"

#import "XMPPManager.h"

@interface RegisterViewController () <XMPPStreamDelegate>

@property (weak, nonatomic) IBOutlet UITextField *registerUNTextField;
@property (weak, nonatomic) IBOutlet UITextField *registerPWTextField;

@end

@implementation RegisterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // 添加代理
    [[XMPPManager shareXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// 按钮点击事件
- (IBAction)registerAction:(id)sender
{
    [[XMPPManager shareXMPPManager] registerWithUsername:self.registerUNTextField.text password:self.registerPWTextField.text];
}

#pragma mark - XMPPStreamDelegate实现协议方法
// 注册成功后,dismiss到登录界面
- (void)xmppStreamDidRegister:(XMPPStream *)sender
{
    NSLog(@"注册成功");
    [self dismissViewControllerAnimated:YES completion:nil];
}

// 注册失败
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error
{
    NSLog(@"注册失败:%@", error);
}

@end

 未完待续...

posted @ 2016-05-18 21:20  墨隐于非  阅读(395)  评论(0编辑  收藏  举报