您的位置:首页 > 移动开发 > IOS开发

iOS XMPP研究探索:登录与注册

2015-03-24 22:08 405 查看
首页了解用户唯一标识有什么组成(JID):

JID 一般由三部分构成:用户名,域名和资源名,例如 test@example.com/Anthony

如果没有设置主机名,则使用 JID 的域名作为主机名

端口号是可选的,默认是 5222

下面动手开始,来创建一个单例,用于全权负责管理与XMPP交互,

@interface HYBXMPPHelper : NSObject <XMPPStreamDelegate, XMPPRosterDelegate>

/**
 * Singleton shared method
 */
+ (HYBXMPPHelper *)shared;

- (BOOL)connect;
- (BOOL)disconnect;
// 上线
- (void)goOnline;
// 下线
- (void)goOffline;

- (void)destroyResources;

@end


还得声明这几个属性,在匿名扩展中添加,无须对外公开:

@property (nonatomic, strong) XMPPStream *xmppStream;
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;

// 花名册相关
@property (nonatomic, strong) XMPPRoster *xmppRoster;
@property (nonatomic, strong) XMPPRosterCoreDataStorage *xmppRosterStorage;


下面来实现单例方法:

+ (HYBXMPPHelper *)shared {
  static HYBXMPPHelper *sg_xmppSharedObject = nil;
  static dispatch_once_t onceToken;
  
  dispatch_once(&onceToken, ^{
    if (!sg_xmppSharedObject) {
      sg_xmppSharedObject = [[self alloc] init];
      [sg_xmppSharedObject setupXmppStream];
    }
  });
  
  return sg_xmppSharedObject;
}


在初始化时,配置一下:(后面需要的属性,也先贴出来)

- (void)setupXmppStream {
  NSAssert(_xmppStream == nil, @"-setupXmppStream method called multiple times");
  
  _jid = [[NSUserDefaults standardUserDefaults] objectForKey:kUserJIDKey];
  _password = [[NSUserDefaults standardUserDefaults] objectForKey:kUserPasswordKey];
  
  
  _xmppStream = [[XMPPStream alloc] init];
  
#if !TARGET_IPHONE_SIMULATOR
  // 设置此行为YES,表示允许socket在后台运行
  // 在模拟器上是不支持在后台运行的
  _xmppStream.enableBackgroundingOnSocket = YES;
#endif
  
  // XMPPReconnect模块会监控意外断开连接并自动重连
  _xmppReconnect = [[XMPPReconnect alloc] init];
  
  // 配置花名册并配置本地花名册储存
  _xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
  _xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:_xmppRosterStorage];
  _xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;
  _xmppRoster.autoFetchRoster = YES;
  
  // 配置vCard存储支持,vCard模块结合vCardTempModule可下载用户Avatar
  _xmppvCardStorage = [[XMPPvCardCoreDataStorage alloc] init];
  _xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:_xmppvCardStorage];
  _xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_xmppvCardTempModule];
  
  // XMPP特性模块配置,用于处理复杂的哈希协议等
  _xmppCapailitiesStorage = [[XMPPCapabilitiesCoreDataStorage alloc] init];
  _xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:_xmppCapailitiesStorage];
  _xmppCapabilities.autoFetchHashedCapabilities = YES;
  _xmppCapabilities.autoFetchNonHashedCapabilities = NO;
  
  // 激活XMPP stream
  [_xmppReconnect activate:_xmppStream];
  [_xmppRoster activate:_xmppStream];
  [_xmppvCardTempModule activate:_xmppStream];
  [_xmppvCardAvatarModule activate:_xmppStream];
  [_xmppCapabilities activate:_xmppStream];
  
  // 消息相关
  _xmppMessageStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
  _xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_xmppMessageStorage];
  [_xmppMessageArchiving setClientSideMessageArchivingOnly:YES];
  [_xmppMessageArchiving activate:_xmppStream];
  
  // 添加代理
  [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
  [_xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
  [_xmppMessageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
}


在应用将要终止时,释放资源等:

- (void)destroyResources {
  [_xmppStream removeDelegate:self];
  [_xmppRoster removeDelegate:self];
  [_xmppReconnect deactivate];
  [_xmppRoster deactivate];
  [_xmppvCardTempModule deactivate];
  [_xmppvCardAvatarModule deactivate];
  [_xmppCapabilities deactivate];
  [_xmppMessageArchiving deactivate];
  
  [_xmppStream disconnect];
  
  _xmppStream = nil;
  _xmppRoster = nil;
  _xmppReconnect = nil;
  _xmppRosterStorage = nil;
  _xmppvCardAvatarModule = nil;
  _xmppvCardStorage = nil;
  _xmppvCardTempModule = nil;
  _xmppCapabilities = nil;
  _xmppCapailitiesStorage = nil;
  _xmppMessageStorage = nil;
  _xmppMessageArchiving = nil;
}


调用只在appdelegate中调用:

- (void)applicationWillTerminate:(UIApplication *)application {
  [[HYBXMPPHelper shared] destroyResources];
}


接下来就是需要实现代理方法。

先对外公开一个注册API,然后实现:

- (void)registerWithJid:(NSString *)jidString
               password:(NSString *)password
             completion:(HYBCompletionBlock)completion {
  _isLogining = NO;
  
  if (_isRegistering) {
    return;
  }
  
  if (jidString == nil || password == nil) {
    if (completion) {
      completion(NO, @"用户名或者密码不能为空");
    }
    return;
  }
  
  _jid = jidString;
  _password = password;
  self.completionBlock = completion;

  _isRegistering = YES;
  if (![_xmppStream isConnected]) {
    if (![self connect]) {
      _isRegistering = NO;
      
      if (completion) {
        completion(NO, @"连接服务器失败");
      }
    }
    return;
  }
  
  [self registerWithJid:jidString];
}

- (void)registerWithJid:(NSString *)jidString {
  if (![jidString hasSuffix:kServer]) {
    jidString = [NSString stringWithFormat:@"%@@%@", jidString, kServer];
  }
  [_xmppStream setMyJID:[XMPPJID jidWithString:jidString]];
  
  // 设置服务器
  [_xmppStream setHostName:kServer];
  
  NSError *error = nil;
  _isRegistering = YES;
  if (![_xmppStream registerWithPassword:_password error:&error]) {
    NSLog(@"注册账号失败:%@", [error description]);
    _isRegistering = NO;
    if (self.completionBlock) {
      self.completionBlock(NO, [error description]);
    }
  }
}


如果没有连接服务器,就需要先连接服务器:

- (BOOL)connect {
  if (![_xmppStream isDisconnected]) {
    return NO;
  }
  
  if (_jid == nil || _password == nil) {
    return NO;
  }
  
  [_xmppStream setMyJID:[XMPPJID jidWithString:_jid]];
  [_xmppStream setHostName:kServer];
  
  // 连接
  NSError *error = nil;
  if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error]) {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error connecting"
                                                        message:@"See console for error details."
                                                       delegate:nil
                                              cancelButtonTitle:@"Ok"
                                              otherButtonTitles:nil];
    [alertView show];
    
    NSLog(@"Error connecting: %@", [error description]);
    
    return NO;
  }
  
  return YES;
}


然后就是注册成功与失败的代理了:

// @begin
// 注册相关
- (void)xmppStreamDidRegister:(XMPPStream *)sender {
  NSLog(@"注册成功");
  
  if (self.completionBlock && _isRegistering) {
    self.completionBlock(YES, nil);
  }
  
  _isRegistering = NO;
}

- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
  /*
   <iq xmlns="jabber:client" type="error" to="127.0.0.1/347f0596">
   <query xmlns="jabber:iq:register">
   <username>test</username>
   <password>test</password>
   </query>
   <error code="409" type="cancel">
   <conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></conflict>
   </error>
   </iq>
   */
  NSLog(@"注册失败: %@", error);
  DDXMLElement *query = [error elementForName:@"query"];
  DDXMLElement *username = [query elementForName:@"username"];
  DDXMLElement *password = [query elementForName:@"password"];
  
  DDXMLElement *errorNode = [error elementForName:@"error"];
  int code = [errorNode attributeIntValueForName:@"code"];
  NSLog(@"%@ %@", [username stringValue], [password stringValue]);
  if (self.completionBlock && _isRegistering && !_isLogining) {
    self.completionBlock(NO, [self errorMessageWithErrorCode:code]);
  }
  
  _isRegistering = NO;
}
// @end


下面看看登录:

1、先对外公开一个登录API:

- (void)loginWithJid:(NSString *)jidString
            password:(NSString *)password
          completion:(HYBCompletionBlock)completion {
  _isRegistering = NO;
  
  if (_xmppStream.isAuthenticated) {// 已经登录了,就无须再登录
    if (completion) {
      completion(YES, nil);
    }
    return;
  }
  
  if (_isLogining) {
    return;
  }
  
  if (jidString == nil || password == nil) {
    if (completion) {
      completion(NO, @"用户名或者密码不能为空");
    }
    return;
  }
  
  _jid = jidString;
  _password = password;
    self.completionBlock = completion;
  _isLogining = YES;
  
  if (![_xmppStream isConnected]) {
    if (![self connect]) {
      _isLogining = NO;
      
      if (completion) {
        completion(NO, @"连接服务器失败");
      }
    }
    return;
  }
  
  [self loginWithJid:jidString];
}

- (void)loginWithJid:(NSString *)jidString {
  if (![jidString hasSuffix:kServer]) {
    jidString = [NSString stringWithFormat:@"%@@%@", jidString, kServer];
  }
  [_xmppStream setMyJID:[XMPPJID jidWithString:jidString]];
  
  // 设置服务器
  [_xmppStream setHostName:kServer];
  
  NSError *error = nil;
  _isLogining = YES;
  if (![_xmppStream authenticateWithPassword:_password error:&error]) {
    NSLog(@"登录失败:%@", [error description]);
    _isLogining = NO;
    if (self.completionBlock) {
      self.completionBlock(NO, [error description]);
    }
  }
}


然后会在代理中回调:

// @begin
// 登录相关
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
  if (_isRegistering) {
    [self registerWithJid:_jid];
    return;
  }
  
  // 登录成功
  if (self.completionBlock && _isLogining) {
    self.completionBlock(YES, nil);
  }
  
  NSLog(@"密码校验成功,用户将要上线");
  _isLogining = NO;
  [self goOnline];
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error {
  if (_isRegistering) {
    [self registerWithJid:_jid];
    return;
  }
  
  if (_isLogining) {
    [self loginWithJid:_jid];
    return;
  }
  
  _isLogining = NO;
  NSLog(@"didNotAuthenticate :密码校验失败,登录不成功,原因是:%@", [error XMLString]);
}
// @end


当登录成功后,发送 我上线的 通知给我的好友们:

- (void)goOnline {
  // 获取现场节点,默认type = "available"
  XMPPPresence *presence = [XMPPPresence presence];
  NSString *domain = [_xmppStream.myJID domain];
  
  // Google set their presence priority to 24, so we do the same to be compatible.
  if ([domain isEqualToString:@"gmail.com"]
      || [domain isEqualToString:@"gtalk.com"]
      || [domain isEqualToString:@"talk.google.com"]) {
    NSXMLElement *priority = [NSXMLElement elementWithName:@"priority"
                                               stringValue:@"24"];
    [presence addChild:priority];
  }
  
  // 发送下线信息
  [_xmppStream sendElement:presence];
}


到此就完成了登录、注册功能了!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: