zoukankan      html  css  js  c++  java
  • XMPP

    一.什么是XMPP

    XMPP是一种基于XML的即时通讯协议,前身是jabber.

    二.为什么要写XMPP单例

    由于XMPPStream是基于myJID建立的长连接,因此在应用程序中应该仅被实例化一次(也就是建立单例)

    + (id)shareInstance

    {

        static id _s;

        if (_s== nil) {

            _s = [[[self class]alloc]init];

        }

        return _s;

    }

    三.XMPP初始化

    //初始化用户

            _currentUser = [[UserModel alloc]init];

            _allFriendList = [[NSMutableArray alloc]init];

            _xmppStream = [[XMPPStream alloc]init];

            //设置主机名

            [_xmppStream setHostName:@"1000phone.net"];

            //设置端口

            [_xmppStream setHostPort:5222];

            //在这里并没有连接

            //设置代理,有数据回来急用delegate接收

    //        xxx.delegate = self; 是一样的

            //这里使用了一个多代理,多对多的方式

            [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

    四.用户模型

      @property (nonatomic,copy)NSString *jid;

        @property (nonatomic,copy)NSString *password;

    @property (nonatomic,copy)NSString *status;

    五.XMPP注册

    //保存注册回调

    void (^saveRegCb)(BOOL ret,NSError *error);

    - (void)registerUser:(NSString *)jid withPassword:(NSString *)passWord withCompletion:(void (^)(BOOL, NSError *))cb

    {

        saveRegCb = [cb copy];

        isInRegisting = YES;

        //真正的注册

        if ([_xmppStream isConnected]) {

            [_xmppStream disconnect];

        }

        _currentUser.jid = jid;

        _currentUser.password = passWord;

        /*

         1.给服务器账号

         2.服务器返回我收到了账号

         3.再给服务器密码

         */

        //把jid字符串转化为XMPPJID对象

        XMPPJID *myjid = [XMPPJID jidWithString:jid];

        //连接服务器

        //给xmppStream流设置一个账号

        [_xmppStream setMyJID:myjid];

        NSError *error = nil;

        //异步方式,只是去连,但还没有成功

        //connectWithTimeout,这个函数绝对不能调用两次

        BOOL ret = [_xmppStream connectWithTimeout:-1 error:&error];

        //异步注册,需要代理

        if (ret == NO) {

            if (saveRegCb) {

                saveRegCb(NO,error);

            }

        }

    }

    //已经连接

    - (void)xmppStreamDidConnect:(XMPPStream *)sender

    {

        NSLog(@"连接上了 %@",NSStringFromSelector(_cmd));

        //输入密码

        NSError *error= nil;

        //因为不论注册还是登录都会走这个方法,所以用一个变量来区分

        if (isInRegisting) {

            //一旦注册就会回到xmppStreamDidRegister

              [_xmppStream registerWithPassword:_currentUser.password error:&error];

        }else

        {

            //对于已有的用户,这里是授权密码

            [_xmppStream authenticateWithPassword:_currentUser.password error:&error];

            //一旦授权完成,就会进入密码授权正确

        }

      

        //成功和失败

    }

    //注册成功

    - (void)xmppStreamDidRegister:(XMPPStream *)sender

    {

        NSLog(@"function %@",NSStringFromSelector(_cmd));

        if (saveRegCb) {

            saveRegCb(YES,nil);

        }

    }

    //注册失败

    - (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error

    {

        NSLog(@"function %@",NSStringFromSelector(_cmd));

        NSLog(@"error is %@",[error description]);

        if (saveRegCb) {

            //因为error 是NSXMLElement类型,所以要转化

            NSError *myerror = [NSError errorWithDomain:error.description code:-1 userInfo:nil];

            saveRegCb(NO,myerror);

        }

    }

    六.XMPP登录

    //保存登录回调

    void (^saveLoginCb)(BOOL ret,NSError *error);

    - (void)loginUser:(NSString *)jid withPassword:(NSString *)passWord withCompletion:(void (^)(BOOL, NSError *))cb

    {

        saveLoginCb = [cb copy];

        isInRegisting = NO;

        //登录分为两步

        //1.给服务器输入账号,2给服务器输入密码

        _currentUser.jid = jid;

        _currentUser.password =passWord;

        XMPPJID *myjid = [XMPPJID jidWithString:jid];

        //有可能是注册完了就登录,所以要断开

        if ([_xmppStream isConnected]) {

            [_xmppStream disconnect];

        }

        [_xmppStream setMyJID:myjid];

        NSError *error= nil;

       BOOL ret= [_xmppStream connectWithTimeout:-1 error:&error];

        if (ret == NO) {

            saveLoginCb(NO,error);

        }

    }

    //授权成功

    - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender

    {

        NSLog(@"function %@",NSStringFromSelector(_cmd));

        //可以上线了

        if (saveLoginCb) {

            saveLoginCb(YES,nil);

        }

        //上线,获取所有好友

        [self goOnLine];

        //取得所有好友信息

        //取得当前上线是谁

        [self getAllFriends];

    }

    //授权失败

    - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error

    {

        NSLog(@"function %@",NSStringFromSelector(_cmd));

        if (saveLoginCb) {

            saveLoginCb(NO,[NSError errorWithDomain:error.description code:-1 userInfo:nil]);

        }

    }

    七.XMPP上线

    - (void)goOnLine

    {

        //presence   曝光

        //这个类的作用就是产生一个xml的结构

        XMPPPresence *p = [XMPPPresence presence];

        //通过给服务器发送XML文件的形式,上线

        [_xmppStream sendElement:p];

    }

    八.XMPP获取好友

    void (^saveFriendCb)(NSArray *);

    NSMutableArray *_allFriendList;

    - (void)getAllFriends

    {

        //产生一个节点<query xxx>

        NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"];

        NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];

        [iq addAttributeWithName:@"type" stringValue:@"get"];

        [iq addChild:query];

        NSLog(@"all friends %@",iq);

        //传给服务器,服务器返回的也是xml

        [_xmppStream sendElement:iq];

    }

    #pragma mark - 获取好友(默认是离线状态)

    - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq

    {

    //    NSLog(@"iq is %@",iq);

        // XML解析好友列表

        NSXMLElement * query = iq.childElement;

        for (NSXMLElement * oneChild in query.children) {

            NSString * jid = [oneChild attributeStringValueForName:@"jid"];

            // 相同JID好友防止重复添加 默认情况下都是离线好友

    [self addFriendFromReceiveIQ:jid withStatus:@"unavailable"];

        }

        

        return YES;

    }

    #pragma mark - 更新好友列表 好友状态

    - (void)addFriendFromReceiveIQ:(NSString *)name withStatus:(NSString *)status

    {

        for (UserModel * m in _friendArr) {

            if ([m.name isEqualToString:name]) {

                return;

            }

        }

        

        UserModel * m = [[UserModel alloc] init];

        m.name = name;

        m.status = status;

        [_friendArr addObject:m];

        if (saveFriendCb) {

            saveFriendCb(_friendArr);

        }

    }

    //获取状态更新,这个方法会比获取好友更先走

    #pragma mark - 订阅事件  上线 离线 发送好友请求 拒绝好友请求...

    /*

    available -- (默认)用户空闲状态

    unavailable -- 用户没空看消息

    subscribe -- 请求订阅别人,即请求加对方为好友

    subscribed -- 统一被别人订阅,也就是确认被对方加为好友

    unsubscribe -- 他取消订阅别人,请求删除某好友

    unsubscribed -- 拒绝被别人订阅,即拒绝对放的添加请求

    error -- 当前状态packet有错误

     */

    - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence

    {

    //    maozhi4@10.2.159.91/747f21ab 变成 maozhi4@10.2.159.91

        // 从presence中提取出谁发送了 上线 下线请求

    //    [presence attributeStringValueForName:@"from"]

        XMPPJID * fromJid = [XMPPJID jidWithString:[presence attributeStringValueForName:@"from"]];

        NSString * jidStr = [NSString stringWithFormat:@"%@@%@",fromJid.user,fromJid.domain];

        //XMPPJID * jid = [XMPPJID jidWithString:jidStr];

        

        NSString * status = @"available";

        NSString * type = [presence type];

        if ([type isEqualToString:@"unavailable"]){

            // 离线

            status = @"unavailable";

        }

        else if ([type isEqualToString:@"available"]){

            // 上线

            status = @"available";

        }

    [self updateOrAddFriendFromReceivePresence:jidStr withStatus:status];

        NSLog(@">>%@ %@",jidStr,status);

    }

    - (void)updateOrAddFriendFromReceivePresence:(NSString *)name withStatus:(NSString *)status

    {

        BOOL exist = NO;

        // 如果有相同JID的好友 不应该添加至数组

        for (UserModel * m in _friendArr) {

            if ([m.name isEqualToString:name]) {

                exist = YES;

                m.status = status;

                break;

            }

        }

        // 可以被添加至数组

        if (!exist) {

            UserModel * m = [[UserModel alloc] init];

            m.name = name;

            m.status = status;

            [_friendArr addObject:m];

        }

        

        // 回调好友列表

        if (saveFriendCb) {

            saveFriendCb(_friendArr);

        }

    }

    - (void)getAllFriends:(void (^)(NSArray *))cb

    {

        saveFriendCb = [cb copy];

        //好友会不断上线下线

        if (saveFriendCb) {

            saveFriendCb(_allFriendList);

        }

    }

    九.登录页面

    - (IBAction)login:(id)sender {

        //登录

        [[QFXMPPmanager shareInstance]

         loginUser:self.nameField.text

         withPassword:self.passField.text

         withCompletion:^(BOOL ret,NSError *error)

         {

             if (ret == YES) {

                 NSLog(@"登录成功");

                 FriendListViewController *flc = [[FriendListViewController alloc]init];

                 UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:flc];

                 [self presentViewController:nav animated:YES completion:nil];

             }else

             {

                 NSLog(@"登录失败");

             }

         }];

    }

    - (IBAction)regist:(id)sender {

        //注册

        [[QFXMPPmanager shareInstance]

         registerUser:self.nameField.text

        withPassword:self.passField.text

        withCompletion:^(BOOL ret,NSError *error)

        {

            if (ret == YES) {

                NSLog(@"注册成功");

            }else

            {

                NSLog(@"注册失败");

            }

        }];

    }

    十.获取好友页面

    - (void)viewDidLoad

    {

        [super viewDidLoad];

        self.title = @"朋友";

        [[QFXMPPmanager shareInstance]getAllFriends:^(NSArray *list)

         {

             [_allFriendList removeAllObjects];

             [_allFriendList addObjectsFromArray:list];

             [self.tableView reloadData];

         }];

    }

    十一.接收消息页面

    [[XMPPmanager shareInstace]refreshMessage:^(ChatModel *model) {

            //来一条消息,刷新一条消息

            [_chatList addObject:model];

            [self.tableView reloadData];

        }];

    十二.聊天模型

    @property (nonatomic,copy)NSString *body;

    @property (nonatomic,copy)NSString *from;

    @property (nonatomic,copy)NSString *to;

    十三.收到的消息

    - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message

    {

        //判断是否是聊天消息

        if ([message isChatMessage])

        {

            //判断消息是否带消息体(body)

            if ([message isChatMessageWithBody])

            {

                NSString *body = [message body];

                NSString *from = [[message from]bare];

                NSString *to = [[message to]bare];

                

                NSLog(@"body==%@,from=%@,to=%@",body,from,to);

                

                ChatModel *model = [[ChatModel alloc]init];

                model.body = body;

                model.from = from;

                model.to = to;

                

                //刷表

                if (saveRefreshMessageBlock) {

                    saveRefreshMessageBlock(model);

                }

            }

        }

    }

    十四.发送消息

    在聊天的viewController中:

    [xmppmanager shareinstace](void)sendMessage:(nssrting *)msg  toUser:(userModel *)toUser  withCompletion:(void (^) (bool ret))cb

    - (void)sendMessage:(nssrting *)msg  toUser:(userModel *)toUser  withCompletion:(void (^) (bool ret))cb;

    {

    saveSendMessageBlocks  = [cb copy];

    xmppjid *toJid = [xmppJid jidwithstring:toUser.name];

    xmppMessage *oneMessage = [[xmppmessage alloc]initWithtype:@“chat”to:toJid];

    [oneMessage addBody:msg];

    [_xmppStream sendElement: oneMessage];

    }

    - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message;

    {

    if(saveSendMessageBlocks)

    {

    saveSendMessageBlocks(YES);

    }

    }

    - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error;

    {

    if(saveSendMessageBlocks)

    {

    saveSendMessageBlocks(NO);

    }

    }

  • 相关阅读:
    struts2文件下载的编写步骤(文件导出)和输入流转换的方法
    spring的applicationContext.xml配置SessionFactory抛异常
    引用第三方高德地图接口---使用js脚本进行开发地图定位的步骤
    登陆时不同浏览器获取session存在的相关疑问?
    统一的异常处理和自定义的全局异常处理器的配置使用
    国际化的工具类ognl utils
    oracle中decode的用法(例子)
    PLSQL连接本机oracle 11g 64 数据库的步骤
    处理oracle 报ORA-12505 信息:listener does not currently know of SID given in connect descriptor...
    spring容器的配置和springmvc的配置
  • 原文地址:https://www.cnblogs.com/PengFei-N/p/4703242.html
Copyright © 2011-2022 走看看