XMPP即时聊天步骤

作者: MorpLCP | 来源:发表于2016-01-08 10:22 被阅读2147次
    XMPP核心的几个类  :XMPPStream(通讯管道)、XMPPJID(用户标示)、XMPPPresence(出席通知)、XMPPRoster(花名册)
    

    一.登录注册

    1.创建单例(XMPPManager)

    static XMPPManager *manager = nil;
    + (XMPPManager *)sharedManager{
        // GCD 创建单例对象
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            manager = [[XMPPManager alloc] init];
        });
        return manager;
    }
    

    2.配置通讯管道

    - (instancetype)init{
        self = [super init];
        if (self) {
            //-----------------配置通信管道---------------
            self.stream = [[XMPPStream alloc] init];
            // 设置通信管道的目标服务器地址
            _stream.hostName = kHostName;
            // 设置通信管道的xmpp server端口
            _stream.hostPort = kHostPort;
            // 设置代理
            [_stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
        }
        return self;
    }
    

    3.构造XMPPJID

    // 根据一个用户名构造一个xmppjid
    XMPPJID *myjid = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];
    // 设置通信管道的jid
    _stream.myJID = myjid;
    

    4.连接服务器

    if ([_stream isConnected]) {
            NSLog(@"已经连接");
            [_stream disconnect]; // 断开连接
        }
        BOOL result = [_stream connectWithTimeout:30 error:nil];
        if (result){
            NSLog(@"服务器链接成功");
        }else{
            NSLog(@"服务器链接失败");
        }
    

    5.连接服务器常用代理方法

    // 服务器连接超时
    - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender{
        NSLog(@"服务器连接超时");
    }
    // 断开连接
    - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
        if (error) {
            NSLog(@"%@",error);
        }
        // 离线消息(下线通知)
        XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
        // 发送下线通知
        [_stream sendElement:presence];
    }
    

    6.登陆 先连接服务器,再登陆

    // 连接成功之后发起登陆事件
    [_stream authenticateWithPassword:self.loginPassword error:nil];
    

    7.登陆常用代理方法

    // 登陆成功
    - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
        NSLog(@"登陆成功");
        // 出席消息(上线通知)
        XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
        // 发送上线通知
        [_stream sendElement:presence];
    }
    // 登陆失败
    - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
        NSLog(@"登陆失败:%@",error);
    }
    

    8.注册 先连接服务器,再注册

    // 连接成功之后发起注册事件
    
                [_stream registerWithPassword:self.regPassword error:nil];
    

    9.注册常用的代理方法

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

    二. 好友列表、添加好友、发送消息、接收消息

    1.单例中创建花名册的属性 (XMPPRoster 可以处理和好友相关的事:获取好友列表,添加好友,接收好友请求,同意添加好友,拒绝添加好友)

    /**
     *  好友花名册,用来处理和好友相关的事件
     */
    @property(nonatomic, strong) XMPPRoster * roster;
    

    2.初始化一个花名册,并且在通讯管道中激活花名册

    //------------------用户花名册----------------
    
            // xmpp为我们提供了一个CoreData存储器
    
            XMPPRosterCoreDataStorage *xrcds = [XMPPRosterCoreDataStorage sharedInstance];
    
            // 创建roster 花名册时,需要给花名册指定一个数据存储的地方(就是XMPPRosterCoreDataStorage)
    
            self.roster = [[XMPPRoster alloc] initWithRosterStorage:xrcds dispatchQueue:dispatch_get_main_queue()];
    
            // 在通讯管道中激活花名册
    
            // 这时就可以通过通讯管道去给服务器发送请求了。
    
            // 然后roster的消息都通过stream间接的发给服务器
    
            [self.roster activate:self.stream];
    

    3.设置花名册代理,在好友列表控制器里面设置,列表控制器遵循代理 XMPPRosterDelegate// 设置代理

        [[XMPPManager sharedManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
    

    4.声明一个数组存放好友

    @property (nonatomic, strong) NSMutableArray *rosters;
    

    5.花名册常用代理方法

    // 开始接收好友列表
    - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{
        NSLog(@"开始接收好友列表");
    }
    
    // 接收完毕
    - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{
        NSLog(@"结束接收好友列表");
    }
    
    // 每次接收到一个好友就会走一次这个方法
    - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{
    
        NSString *jid = [[item attributeForName:@"jid"] stringValue];
    
        XMPPJID *xmppjid = [XMPPJID jidWithString:jid resource:kResource];
    
        [self.rosters addObject:xmppjid];
    
        NSIndexPath *indexpath = nil;
    
        if (self.rosters.count == 0) return;
    
        indexpath = [NSIndexPath indexPathForRow:self.rosters.count - 1 inSection:0];
    
        [self.tableView insertRowsAtIndexPaths:@[indexpath] withRowAnimation:(UITableViewRowAnimationLeft)];
    
    }
    
    // 收到添加好友请求 :(同意:[roster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];)
    
    //(拒绝:[roster rejectPresenceSubscriptionRequestFrom:presence.from];)
    
    - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
    
        XMPPRoster *roster = [XMPPManager sharedManager].roster;
    
        NSString *message = [NSString stringWithFormat:@"%@请求加你为好友", presence.from.user];
    
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"好友请求" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    
        UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
            // 同意请求
            [roster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];
        }];
    
        UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"拒绝" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
            // 拒绝请求
            [roster rejectPresenceSubscriptionRequestFrom:presence.from];
        }];
    
        [alert addAction:action1];
        [alert addAction:action2];
        [self presentViewController:alert animated:YES completion:nil];
    }
    

    6.添加好友(添加好友界面)

    - (IBAction)actionAdd:(UIButton *)sender {
        // 拿到花名册管理类
        XMPPRoster *roster = [XMPPManager sharedManager].roster;
        // 拼装要添加的好友
        XMPPJID *userjid = [XMPPJID jidWithUser:self.txtUserName.text domain:kDomin resource:kResource];
        // 添加好友请求
        [roster subscribePresenceToUser:userjid];
    }
    

    7.建立聊天

    7.1 声明属性 (声明一个XMPPJID类型的属性,记录要聊天的对象)

    // 当前聊天对象
    @property(nonatomic, strong) XMPPJID * jidChatTo;
    

    7.2 选择聊天对象(可视化编程中)

    点击好友列表页面中的一个好友,将这个好友的XMPPJID传给聊天页面。
     - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
         if ([sender isKindOfClass:[UITableViewCell class]]) {
             NSIndexPath *index = [self.tableView indexPathForCell:(UITableViewCell *)sender];
             XMPPJID *jid = self.rosters[index.row];
             // 属性传值
             ChatViewController *chatVC = [segue destinationViewController]; // 获取目标控制器
             chatVC.jidChatTo = jid;
         }
    

    7.3 声明聊天界面属性

    声明聊天相关的控件属性以及其他属性
    @property (weak, nonatomic) IBOutlet UITableView *tableChat; // 展示聊天记录
    
    @property (weak, nonatomic) IBOutlet UITextField *txtMessage; // 输入消息内容
    
    @property (weak, nonatomic) IBOutlet UIView *viewInput; // 底部的输入控件
    
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *constraintBottomFromSuper; // 底部frame约束
    
    @property (nonatomic, strong) NSMutableArray *messages; // 存放聊天信息
    

    7.4 获取聊天记录

    单例(XMPPManager)里面声明CoreData的上下文对象属性,以及聊天消息的归档处理类的对象属性
    /**
     *  消息归档处理类
     *  程序关闭后,下次打开还可以再次查看以前的聊天记录
     */
    @property(nonatomic, strong) XMPPMessageArchiving * messageArchiving;
    
    /**
     *  coredata 上下文,用来获取通过messageArchiving归档后存储起来的消息
     */
    @property(nonatomic, strong) NSManagedObjectContext * context;
    

    单例对象初始化(init方法)里面对上下文,和归档处理对象的一些操作

    //------------------初始化 XMPPMessageArchiving------
            // xmppMessageArchiving的主要功能:1、通过通讯管道获取到服务器发送过来的消息。2、将消息存储到指定的XMPPMessageArchivingCoreStorage
            // xmpp为我们提供的一个存储聊天消息的coredata仓库
            XMPPMessageArchivingCoreDataStorage *xmacds = [XMPPMessageArchivingCoreDataStorage sharedInstance];
            // 初始化时,需要给这个归档类指定一个存储仓库
            self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:xmacds dispatchQueue:dispatch_get_main_queue()];
            // 在通讯管道中激活
            [self.messageArchiving activate:self.stream];
            // 获取消息归档类提供的上下文信息
            self.context = xmacds.mainThreadManagedObjectContext;
    
    聊天页面里面通过单例中获得的上下文对象,从CoreData中取到聊天记录数据
    // 加载所有信息(通过单例类的上下文获取)
    - (void)reloadAllMessage{
        // 获取上下文信息
        NSManagedObjectContext *context = [XMPPManager sharedManager].context;
        // xmppMessageArchving : 把接收到得消息归档,归档后的数据类型是:XMPPMessageArchiving_Message_CoreDataObject
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
        // 设置断言
        // 查找所有的和当前聊天对象的聊天记录
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr = %@",self.jidChatTo.bare];
        // 让断言生效
        [fetchRequest setPredicate:predicate];
        // 获取数据
        NSArray *array = [context executeFetchRequest:fetchRequest error:nil];
        if (array) {
            // 将原来的数据清空
            [self.messages removeAllObjects];
        }
        // 把获取的数据添加到当前数据源中
        [self.messages addObjectsFromArray:array];
        // 刷新列表
        [self.tableChat reloadData];
        // 将视图定位到最新的一条消息。
        if (array.count > 0) {
            NSIndexPath *index = [NSIndexPath indexPathForItem:array.count - 1 inSection:0];
            [self.tableChat scrollToRowAtIndexPath:index atScrollPosition:(UITableViewScrollPositionBottom) animated:YES];
        }
    }
    
    展示聊天内容
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
        // 获取对应的消息
        XMPPMessageArchiving_Message_CoreDataObject *message = self.messages[indexPath.row];
        // 判断是不是自己发出去的
        if ([message isOutgoing]) {
            cell.textLabel.text = [NSString stringWithFormat:@"我:%@",message.body];
        }else{
            cell.textLabel.text = [NSString stringWithFormat:@"%@:%@",message.bareJidStr, message.body];
        }
        return cell;
    }
    

    7.5 发送和接收消息

    设置通讯管道代理
    XMPPStream *stream = [XMPPManager sharedManager].stream;
    [stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    通讯管道代理中和发送以及接收消息相关的代理方法
    // 收到信息
    - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
        NSLog(@"接收到一条消息:%@",message);
        [self reloadAllMessage]; // 加载一遍所有的聊天数据
    }
    // 发送信息
    - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{
        NSLog(@"消息【%@】发送成功",message);
        [self reloadAllMessage]; // 加载一遍所有聊天数据
    }
    
    发送消息
    - (IBAction)actionSendMsg:(UIButton *)sender {
        XMPPStream *stream = [XMPPManager sharedManager].stream;
        // 实例化一个消息类
        XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.jidChatTo];
        // 设置消息内容
        [message addBody:self.txtMessage.text];
        // 通过通讯管道发送
        [stream sendElement:message];
    }
    

    其他:处理键盘弹出相关:

    注册键盘frame改变的通知:
    // 通过通知中心来观察键盘的frame的变化,当键盘frame发生变化后触发keyboardFrameChange事件
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChange:) name:UIKeyboardDidChangeFrameNotification object:nil];
    
    处理方法及键盘回收代理事件:
    // 键盘frame改变后触发事件
    - (void)keyboardFrameChange:(NSNotification *)sender{
        // 键盘改变后的frame
        CGRect rect = [[sender.userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
        // 计算出聊天窗口的底部偏移量
        CGFloat height = self.view.frame.size.height - rect.origin.y;
        self.constraintBottomFromSuper.constant = height;
    }
    #pragma mark -UITextFieldDelegate
    - (BOOL)textFieldShouldReturn:(UITextField *)textField{
        [textField resignFirstResponder];
        return YES;
    }
    

    相关文章

      网友评论

      • 雨O兮:大神有这个源码么,能发份否 (584532675@qq.com)

      本文标题:XMPP即时聊天步骤

      本文链接:https://www.haomeiwen.com/subject/mbifkttx.html