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);

}

}

原文地址:https://www.cnblogs.com/PengFei-N/p/4703242.html