【原】iOS學習42即時通訊之XMPP(1)

1. 即時通訊

 1> 概述

  即時通信(Instant Messaging)是目前Internet上最爲流行的通信方式,各類各樣的即時通信軟件也層出不窮,服務提供商也提供了愈來愈豐富的通信服務功能。html

  即時通信有多種實現方式,例如:XMPP、環信、融雲等。
git

 2> XMPP(可擴展消息處理現場協議)

  XMPP(XML Messages Presence Protocol(可擴展消息處理現場協議))是基於可擴展標記語言(XML)的協議,可用於即時通訊(IM)。github

 3> XMPP的優勢

  • 開放

  XMPP協議是由JSF開源社區組織開發的,不屬於任何的機構和我的,從根本上保證了其開放性。
安全

  • 標準

  互聯網工程工做組將XMPP於2002年正式列爲承認的實時通信及Presence技術。
服務器

  • 分佈式的網絡架構

  XMPP 協議雖然是基於Client/Server 架構,可是XMPP協議自己並無這樣的限制。網絡的架構和電子郵件十分類似,但沒有結合任何特定的網絡架構,適用範圍很是普遍。網絡

  • 具備良好的擴展性

  基於XML 創建起來的應用具備良好的語義完整性和擴展性。
架構

  • 具備很好的彈性

  XMPP 除了可用在即時通訊的應用程序,還能用在網絡管理、內容供稿、協同工具、檔案共享、遊戲、遠端系統監控等。dom

  • 安全性

  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

 未完待續...

相關文章
相關標籤/搜索