iOS(五)基于XMPP的聊天:一

首先先来段废话介绍一下XMPP

全称:可扩展通讯和表示协议
简介:可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯、表示和需求响应服务中的XML数据元流式传输。XMPP以Jabber协议为基础,而Jabber是即时通讯中常用的开放式协议。XMPP is the IETF's formalization of the base XML streaming protocols for instant messaging and presence developed within the Jabber open-source community in 1999,XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。XMPP目前被IETF国际标准组织完成了标准化工作。标准化的核心结果分为两部分;核心的XML流传输协议基于XMLFreeEIM流传输的即时通讯扩展应用
XMPP的核心XML流传输协议的定义使得XMPP能够在一个比以往网络通信协议更规范的平台上。借助于XML易于解析和阅读的特性,使得XMPP的协议能够非常漂亮。
XMPP的即时通讯扩展应用部分是根据IETF在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如AIM,QQ等有功能完整,完善等先进性。
XMPP的扩展协议Jingle使得其支持语音和视频。
反正我知道可以用来聊天就够了,暂时我也不想知道它的代码是写的如何,只要会用我就满足了(介绍这个App的时候会讲明如何用XMPPFramework这个包),当然了再编写代码前,先把XMPPFramework添加到我们的工程里
我构思的App界面还是用到了UITabBarController,有三个子控制器组成,分别是好友列表,群组,个人,好友列表RosterViewController继承UITableView 顾名思义就是和QQ的好友列表类似,存着好友的名字,点击就会跳转到聊天界面,,群组ChatRoomController也继承UItableView,和QQ的群组类似,点击一个可以进去群聊;个人,除了一定要做一个退出功能暂时还未想到其它功能;
和上次一样,我还是定义一个继承UITabBarController的ViewController,并将Main.storyboard删除了
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor=[UIColor whiteColor];
    [self.window makeKeyAndVisible];//main window出现在最前端
    self.window.rootViewController=[[ViewController alloc]init];

    return YES;
}
ViewController.h
//  liaoliao
//  Created by LiuJiawei on 15/11/7.
//  Copyright © 2015年 LIUjiawei. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UITabBarController
@end
ViewController.m
//  liaoliao
//
//  Created by LiuJiawei on 15/11/7.
//  Copyright © 2015年 LIUjiawei. All rights reserved.
//

#import "ViewController.h"
#import "PersonController.h"
#import "RosterController.h"
#import "CharRoomController.h"
#import "LoginViewController.h"
#import "AbilityManage.h"
@interface ViewController ()

@end

@implementation ViewController

-(id)init{
    self=[super init];
    if (self){
        NSLog(@"init");
    }
    
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self createViewControllers];//创造了三个viewcontroller
}

-(void)createViewControllers{
    PersonController *personVC=[[PersonController alloc]init];      //个人
    RosterController *rosterVC=[[RosterController alloc]init];      //好友列表
    ChatRoomController *chatroomVC=[[ChatRoomController alloc]init];//群组
    NSArray *array=@[rosterVC,chatroomVC,personVC];
    NSArray *titlearray=@[@"好友",@"群组",@"个人中心"];
    NSArray *imagearray=@[@"roster",@"chatroom",@"person"];
    NSMutableArray *Muarray=[[NSMutableArray alloc] initWithCapacity:3];
    for (int i=0; i<3; i++) {
        UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:array[i]];
        nav.tabBarItem.title=titlearray[i];
        nav.tabBarItem.image=[UIImage imageNamed:imagearray[i]];
        [Muarray addObject:nav];
    }
    self.viewControllers=Muarray;
}

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

@end


但是一个聊天软件不是应该出现登录界面吗?没错,第一个子控制是好友列表RosterController,他会实例化一个类AbilityMange,由他来判断是否有用户在线,假如没有弹出 登录界面

NSNotificationCenter消息通信机制介绍(KVO)作用:NSNotificationCenter是专门供程序中不同类间的消息通信而设置的.
注册通知:即要在什么地方接受消息;[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mytest:) name:@" mytest" object:nil];
参数介绍:
addObserver: 观察者,即在什么地方接收通知;
     selector: 收到通知后调用何种方法;
     name: 通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的。
发送通知:调用观察者处的方法。
[[NSNotificationCenter defaultCenter] postNotificationName:@"mytest" object:searchFriendArray];
参数:
postNotificationName:通知的名字,也是通知的唯一标示,编译器就通过这个找到通知的。
object:传递的参数
注册方法的写法:
- (void) mytest:(NSNotification*) notification
{
id obj = [notification object];//获取到传递的对象
}
好友列表RosterContrller需要注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onlineNotification:) name:@"Ability_online_Noti" object:nil];

#pragma mark - Notification
-(void)onlineNotification:(NSNotification*)notification{
    
    BOOL signin=[notification.object boolValue];
    if (signin){
        NSLog(@"Roster_queryRosterList");
        [[AbilityRoster sharedRoster] queryRosterList];//已经在线了说明登录成功了,所以查找这个帐号拥有的好友
    }
    else{
        NSLog(@"LoginViewController");
        LoginViewController* loginVC=[[LoginViewController alloc]init];//没有在线,需要登录
        [self presentViewController:loginVC animated:YES completion:^{
            
        }];
        
    }
}


在登录界面也有一个注册通知

 LoginViewController

<span style="font-size:14px;"> [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onlineNotification:) name:@"Ability_online_Noti" object:nil];</span>
-(void)onlineNotification:(NSNotification*)notification{
    BOOL online=[notification.object boolValue];
    NSLog(@"onlineNotification");
    if (online){<span style="white-space:pre">			</span>//在线了登录成功了,登录界面可以消失了
        NSLog(@"no-LoginViewController");
       [self dismissViewControllerAnimated:YES completion:^{}];
    }
   }


发送通知都是在AbiityManage类里
-(void)setOnline:(BOOL)online{
    _online=online;
    if (online){
        //发送在线状态
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:YES]];
    }
    else{
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:NO]];
    }
}

现在具体讲讲RosterController->AbilityMange-> LoginViewController->AbilityMange-> RosterController的过程
由于ViewController的控制,子控制器RosterController被先加载进来,当视图出现前注册一个通知,
-(void)viewWillAppear:(BOOL)animated{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onlineNotification:) name:@"Ability_online_Noti" object:nil];
}

视图加载结束后实例化AbilityManage
-(void)viewDidAppear:(BOOL)animated{
    [AbilityManage sharedManager];
}

然后我们AbilityMange类shareManager方法是怎么样的
static AbilityManage *_shareManager=nil;
+(AbilityManage *)sharedManager{
    if (!_shareManager) {
        _shareManager=[[self alloc] init];
        [_shareManager setupXMPP];
    }
    return _shareManager;
}

-(void)setupXMPP{
    _xmppStream = [[XMPPStream alloc] init];
    [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];使AbilityManage有XmppStream的协议
    [_xmppStream addDelegate:[AbilityRoster sharedRoster] delegateQueue:dispatch_get_main_queue()];使AbilityRoster有XmppStream的协议

#if !TARGET_IPHONE_SIMULATOR
    {
        _xmppStream.enableBackgroundingOnSocket = YES;
    }
#endif
    _xmppReconnect = [[XMPPReconnect alloc] init];
    [_xmppReconnect activate:_xmppStream];
    self.online=NO;//一般默认是不在线的,同时触发方法-(void)setOnline:(BOOL)online;
}

触发方法-(void)setOnline:(BOOL)online;发送的object是NO

-(void)setOnline:(BOOL)online{
    _online=online;
    if (online){
        //发送在线状态
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:YES]];
    }
    else{
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:NO]];
    }
}

这个时候由于RosterController有Ability_online_Not的注册通知,由于是NO所以实例化LoginViewController,并且跳转到登录界面
-(void)onlineNotification:(NSNotification*)notification{
    
    BOOL signin=[notification.object boolValue];
    if (signin){
        NSLog(@"Roster_queryRosterList");
        [[AbilityRoster sharedRoster] queryRosterList];
    }
    else{
        NSLog(@"LoginViewController");
        LoginViewController* loginVC=[[LoginViewController alloc]init];
        [self presentViewController:loginVC animated:YES completion:^{
            
        }];
        
    }
}


当输入用户名和密码后,点击登录,点击按钮的方法如下:

<span style="font-size:14px;">-(IBAction)onButtonDone:(id)sender{
    NSString* username=self.username.text;
    NSString* password=self.password.text;
    NSString* host=self.hosttext.text;
    [[NSUserDefaults standardUserDefaults] setObject:username forKey:Username_Key];
    [[NSUserDefaults standardUserDefaults] setObject:password forKey:Password_Key];
    [[NSUserDefaults standardUserDefaults] setObject:host forKey:Host_Key];
    [[NSUserDefaults standardUserDefaults] synchronize];
    NSLog(@"onbuttondone");
    
   [[AbilityManage sharedManager] signinWithUsername:username password:password host:host isregister:NO];</span>
<span style="font-size:14px;">//需要AbilltyManage类的登陆功能!!!!
}</span>

这个时候又回到了AbilityManage类里,她的连接服务器的方法如下:

-(void)signinWithUsername:(NSString *)username password:(NSString *)password host:(NSString *)host isregister:(BOOL)isregister {
    self.username=username;
    self.password=password;
    self.host=host;
    if (![_xmppStream isDisconnected]){
        return;
    }
    _registerAction=isregister;
    
    self.jid=[NSString stringWithFormat:@"%@@%@",self.username,self.host];
    [_xmppStream setMyJID:[XMPPJID jidWithString:_jid resource:@"drrr"]];
    [_xmppStream setHostName:host];
    NSError *error = nil;
    BOOL result=[_xmppStream connectWithTimeout:3.0f error:&error];
    NSLog(@"connect:%d,%@",result,error);
}

连接成功之后 会依次调用XMPPStreamDelegate的方法,方法是前辈们写好的,暂时不用弄懂,就是给服务器发服务器能看懂我看不懂的字符串

2016-01-13 13:15:42:737 Chat[23076:2707] SEND: <?xml version='1.0'?>
      2016-01-13 13:15:42:742 Chat[23076:2707] SEND: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='xxxxx.6655.la'>      

2016-01-13 13:15:42:791 Chat[23076:5407] RECV: <stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="xxxxx.6655.la" id="8a7e501" stream1:lang="en" version="1.0"/>

然后调用AbilityManage类的XMPPStream代理协议
#pragma mark - xmpp delegate
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
    //验证密码
    NSError* error=nil;
    BOOL result;
    if (!_registerAction){
        result=[_xmppStream authenticateWithPassword:self.password error:&error];
    }
    else{
        result=[_xmppStream registerWithPassword:self.password error:&error];
    }
    ///注册只要一次,之后就改为登录,防止在重复连接的时候又去注册
    _registerAction=NO;
    NSLog(@"authenticated:%d,%@",result,error);
}
因为不是注册,而是登陆,所以执行这条语句  result=[_xmppStream authenticateWithPassword:self.password error:&error];

用xmppStream 的authenticateWithPassword方法进行密码验证,成功的话会响应delegate的方法,就是下面这个
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender

//这里我保持着会用就行的立场
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
    //发送在线状态
    XMPPPresence *presence = [XMPPPresence presence];
    [_xmppStream sendElement:presence];
}
发送了presence,也会受到一个presence

-(void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence{
    XMPPJID* jidFrom=[presence from];
    XMPPJID* jidTo=[presence to];
    NSString *presenceFromUser = [jidFrom user];
    NSString* presenceFromJid=[jidFrom bare];
    //取得好友状态
    NSString *presenceType = [presence type]; //online/offline
    //当前用户是否在线
    NSString *userId = [[sender myJID] user];
    if ([presenceFromUser isEqualToString:userId]){
        if ([presenceType isEqualToString:@"available"]){
            self.online=YES;   //重要在这里
        }
        else if ([presenceType isEqualToString:@"unavailable"]){
            self.online=NO;    <span style="font-family: 'Helvetica Neue', Helvetica, STheiti, 微软雅黑, 黑体, Arial, Tahoma, sans-serif, serif;">//重要在这里</span>
        }   
    }
}

online的值变化了,
-(void)setOnline:(BOOL)online{
    _online=online;
    if (online){
        //发送在线状态
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:YES]];
    }
    else{
        [[NSNotificationCenter defaultCenter] postNotificationName:Ability_online_Noti object:[NSNumber numberWithBool:NO]];
    }
}
这个时候由于LoginViewController也实例化了,而他也有通知事件,所以两个对象都会被通知先看RosterViewController
<span style="font-size:14px;">#pragma mark - Notification
-(void)onlineNotification:(NSNotification*)notification{
    BOOL signin=[notification.object boolValue];
    if (signin){
        [[AbilityRoster sharedRoster] queryRosterList];//寻找此账号的好友
    }
    else{
        NSLog(@"LoginViewController");
        LoginViewController* loginVC=[[LoginViewController alloc]init];
        [self presentViewController:loginVC animated:YES completion:^{        }];  
    }
}</span>
由于是YES了,所以执行寻找好友的方法,对于LoginViewController的通知事件呢

-(void)onlineNotification:(NSNotification*)notification{
    BOOL online=[notification.object boolValue];
    NSLog(@"onlineNotification");
    if (online){
        NSLog(@"no-LoginViewController");
       [self dismissViewControllerAnimated:YES completion:^{}];
    }
   }
很识趣的知道自己该miss了,接着就跳到这个画面了:



至于如何添加好友,并将好友信息找到并显示出来,都是另一个AbilityRoster类负责,下一篇再讲




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值